Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>
Config config = new Config(
new JSTypeRegistry(NullErrorReporter.forOldRhino()),
Sets.<String>newHashSet(),
false);
JsDocInfoParser parser = new JsDocInfoParser(
new JsDocTokenStream(typeString),
"typeparsing",
config,
NullErrorReporter.forNewRhino());
return parser.parseTopLevelTypeExpression(parser.next());
}
/**
* Parses a {@link JSDocInfo} object. This parsing method reads all tokens
* returned by the {@link JsDocTokenStream#getJsDocToken()} method until the
* {@link JsDocToken#EOC} is returned.
*
* @return {@code true} if JSDoc information was correctly parsed,
* {@code false} otherwise
*/
boolean parse() {
int lineno;
int charno;
// JSTypes are represented as Rhino AST nodes, and then resolved later.
JSTypeExpression type;
state = State.SEARCHING_ANNOTATION;
JsDocToken token = next();
ExtractionInfo blockInfo = extractBlockComment(token);
token = blockInfo.token;
// If we have a block level comment, record it.
if (blockInfo.string.length() > 0) {
jsdocBuilder.recordBlockDescription(blockInfo.string);
}
// Parse the actual JsDoc.
retry: for (;;) {
switch (token) {
case ANNOTATION:
if (state == State.SEARCHING_ANNOTATION) {
state = State.SEARCHING_NEWLINE;
lineno = stream.getLineno();
charno = stream.getCharno();
String annotationName = stream.getString();
Annotation annotation = annotationNames.get(annotationName);
if (annotation == null) {
parser.addWarning("msg.bad.jsdoc.tag", annotationName,
stream.getLineno(), stream.getCharno());
} else {
// Mark the beginning of the annotation.
jsdocBuilder.markAnnotation(annotationName, lineno, charno);
switch (annotation) {
case AUTHOR:
ExtractionInfo authorInfo = extractSingleLineBlock();
String author = authorInfo.string;
if (author.length() == 0) {
parser.addWarning("msg.js
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>Block(token);
String description = descriptionInfo.string;
jsdocBuilder.recordDescription(description);
token = descriptionInfo.token;
continue retry;
}
case FILE_OVERVIEW:
ExtractionInfo fileOverviewInfo =
extractMultilineTextualBlock(token,
WhitespaceOption.TRIM);
String fileOverview = fileOverviewInfo.string;
if (!jsdocBuilder.recordFileOverview(fileOverview) ||
fileOverviewJSDocInfo != null) {
parser.addWarning("msg.jsdoc.fileoverview.extra",
stream.getLineno(), stream.getCharno());
}
token = fileOverviewInfo.token;
continue retry;
case LICENSE:
case PRESERVE:
ExtractionInfo preserveInfo =
extractMultilineTextualBlock(token,
WhitespaceOption.PRESERVE);
String preserve = preserveInfo.string;
if (preserve.length() > 0) {
if (fileLevelJsDocBuilder != null) {
fileLevelJsDocBuilder.append(preserve);
}
}
token = preserveInfo.token;
continue retry;
case ENUM:
token = next();
lineno = stream.getLineno();
charno = stream.getCharno();
type = null;
if (token != JsDocToken.EOL && token != JsDocToken.EOC) {
type = createJSTypeExpression(
parseAndRecordTypeNode(token));
}
if (type == null) {
type = createJSTypeExpression(newStringNode("number"));
}
if (!jsdocBuilder.recordEnumParameterType(type)) {
parser.addWarning("msg.jsdoc.incompat.type", lineno, charno);
}
token = eatTokensUntilEOL(token);
continue retry;
case EXPORT:
if (!jsdocBuilder.recordExport()) {
parser.addWarning("msg.jsdoc.export",
stream.getLineno(), stream.getCharno());
}
token = eatTokensUntilEOL();
continue retry;
case EXTERNS:
if (!jsdocBuilder.recordExterns()) {
parser.addWarning("msg.jsdoc.externs",
stream.getLineno(), stream.getCharno());
}
token = eatTokensUntilEOL();
continue retry
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>;
case JAVA_DISPATCH:
if (!jsdocBuilder.recordJavaDispatch()) {
parser.addWarning("msg.jsdoc.javadispatch",
stream.getLineno(), stream.getCharno());
}
token = eatTokensUntilEOL();
continue retry;
case EXTENDS:
case IMPLEMENTS:
skipEOLs();
token = next();
lineno = stream.getLineno();
charno = stream.getCharno();
boolean matchingRc = false;
if (token == JsDocToken.LC) {
token = next();
matchingRc = true;
}
if (token == JsDocToken.STRING) {
Node typeNode = parseAndRecordTypeNameNode(
token, lineno, charno, matchingRc);
lineno = stream.getLineno();
charno = stream.getCharno();
typeNode = wrapNode(Token.BANG, typeNode);
if (typeNode != null && !matchingRc) {
typeNode.putBooleanProp(Node.BRACELESS_TYPE, true);
}
type = createJSTypeExpression(typeNode);
if (annotation == Annotation.EXTENDS) {
if (!jsdocBuilder.recordBaseType(type)) {
parser.addWarning(
"msg.jsdoc.incompat.type", lineno, charno);
}
} else {
Preconditions.checkState(
annotation == Annotation.IMPLEMENTS);
if (!jsdocBuilder.recordImplementedInterface(type)) {
parser.addWarning("msg.jsdoc.implements.duplicate",
lineno, charno);
}
}
token = next();
if (matchingRc) {
if (token != JsDocToken.RC) {
parser.addWarning("msg.jsdoc.missing.rc",
stream.getLineno(), stream.getCharno());
}
} else if (token != JsDocToken.EOL &&
token != JsDocToken.EOF && token != JsDocToken.EOC) {
parser.addWarning("msg.end.annotation.expected",
stream.getLineno(), stream.getCharno());
}
} else {
parser.addWarning("msg.no.type.name", lineno, charno);
}
token = eatTokensUntilEOL(token);
continue retry;
case
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> HIDDEN:
if (!jsdocBuilder.recordHiddenness()) {
parser.addWarning("msg.jsdoc.hidden",
stream.getLineno(), stream.getCharno());
}
token = eatTokensUntilEOL();
continue retry;
case NO_ALIAS:
if (!jsdocBuilder.recordNoAlias()) {
parser.addWarning("msg.jsdoc.noalias",
stream.getLineno(), stream.getCharno());
}
token = eatTokensUntilEOL();
continue retry;
case NO_TYPE_CHECK:
if (!jsdocBuilder.recordNoTypeCheck()) {
parser.addWarning("msg.jsdoc.nocheck",
stream.getLineno(), stream.getCharno());
}
token = eatTokensUntilEOL();
continue retry;
case NOT_IMPLEMENTED:
token = eatTokensUntilEOL();
continue retry;
case INHERIT_DOC:
case OVERRIDE:
if (!jsdocBuilder.recordOverride()) {
parser.addWarning("msg.jsdoc.override",
stream.getLineno(), stream.getCharno());
}
token = eatTokensUntilEOL();
continue retry;
case THROWS:
skipEOLs();
token = next();
lineno = stream.getLineno();
charno = stream.getCharno();
type = null;
if (token == JsDocToken.LC) {
type = createJSTypeExpression(
parseAndRecordTypeNode(token));
if (type == null) {
// parsing error reported during recursive descent
// recovering parsing
token = eatTokensUntilEOL();
continue retry;
}
}
// *Update* the token to that after the type annotation.
token = current();
// Save the throw type.
jsdocBuilder.recordThrowType(type);
// Find the throw's description (if applicable).
ExtractionInfo descriptionInfo =
extractMultilineTextualBlock(token);
String description = descriptionInfo.string;
if (description.length() > 0) {
jsdocBuilder.recordThrowDescription(type, description);
}
token = descriptionInfo.token;
continue retry;
case PARAM:
skipEOLs();
token = next();
lineno = stream.getLineno();
charno = stream.getCharno
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>();
type = null;
if (token == JsDocToken.LC) {
type = createJSTypeExpression(
parseAndRecordParamTypeNode(token));
if (type == null) {
// parsing error reported during recursive descent
// recovering parsing
token = eatTokensUntilEOL();
continue retry;
}
skipEOLs();
token = next();
lineno = stream.getLineno();
charno = stream.getCharno();
}
String name = null;
boolean isBracketedParam = JsDocToken.LB == token;
if (isBracketedParam) {
token = next();
}
if (JsDocToken.STRING != token) {
parser.addWarning("msg.missing.variable.name",
lineno, charno);
} else {
name = stream.getString();
if (isBracketedParam) {
token = next();
// Throw out JsDocToolkit's "default" parameter annotation.
// It makes no sense under our type system.
if (JsDocToken.EQUALS == token) {
token = next();
if (JsDocToken.STRING == token) {
token = next();
}
}
if (JsDocToken.RB != token) {
reportTypeSyntaxWarning("msg.jsdoc.missing.rb");
} else if (type != null) {
// Make the type expression optional, if it isn't
// already.
type = JSTypeExpression.makeOptionalArg(type);
}
}
// If the param name has a DOT in it, just throw it out
// quietly. We do not handle the JsDocToolkit method
// for handling properties of params.
if (name.indexOf('.') > -1) {
name = null;
} else if (!jsdocBuilder.recordParameter(name, type)) {
if (jsdocBuilder.hasParameter(name)) {
parser.addWarning("msg.dup.variable.name", name,
lineno, charno);
} else {
parser.addWarning("msg.jsdoc.incompat.type", name,
lineno, charno);
}
}
}
if (name == null) {
token = eatTokensUntilEOL(token);
continue retry;
}
jsdocBuilder.markName(name, lineno, char
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> eatTokensUntilEOL();
continue retry;
case SEE:
ExtractionInfo referenceInfo = extractSingleLineBlock();
String reference = referenceInfo.string;
if (reference.length() == 0) {
parser.addWarning("msg.jsdoc.seemissing",
stream.getLineno(), stream.getCharno());
} else {
jsdocBuilder.addReference(reference);
}
token = referenceInfo.token;
continue retry;
case SUPPRESS:
token = parseSuppressTag(next());
continue retry;
case TEMPLATE:
ExtractionInfo templateInfo = extractSingleLineBlock();
String templateTypeName = templateInfo.string;
if (templateTypeName.length() == 0) {
parser.addWarning("msg.jsdoc.templatemissing",
stream.getLineno(), stream.getCharno());
} else if (!jsdocBuilder.recordTemplateTypeName(
templateTypeName)) {
parser.addWarning("msg.jsdoc.template.at.most.once",
stream.getLineno(), stream.getCharno());
}
token = templateInfo.token;
continue retry;
case VERSION:
ExtractionInfo versionInfo = extractSingleLineBlock();
String version = versionInfo.string;
if (version.length() == 0) {
parser.addWarning("msg.jsdoc.versionmissing",
stream.getLineno(), stream.getCharno());
} else {
if (!jsdocBuilder.recordVersion(version)) {
parser.addWarning("msg.jsdoc.extraversion",
stream.getLineno(), stream.getCharno());
}
}
token = versionInfo.token;
continue retry;
case DEFINE:
case RETURN:
case THIS:
case TYPE:
case TYPEDEF:
skipEOLs();
lineno = stream.getLineno();
charno = stream.getCharno();
token = next();
Node typeNode = parseAndRecordTypeNode(token, lineno, charno);
if (annotation == Annotation.THIS) {
typeNode = wrapNode(Token.BANG, typeNode);
if (typeNode != null && token != JsDocToken.LC) {
typeNode.putBooleanProp(Node.BRACELESS_TYPE, true);
}
}
type =
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> false;
case EOL:
if (state == State.SEARCHING_NEWLINE) {
state = State.SEARCHING_ANNOTATION;
}
token = next();
continue retry;
default:
if (token == JsDocToken.STAR && state == State.SEARCHING_ANNOTATION) {
token = next();
continue retry;
} else {
state = State.SEARCHING_NEWLINE;
token = eatTokensUntilEOL();
continue retry;
}
}
// next token
token = next();
}
}
/**
* Parse a {@code @suppress} tag of the form
* {@code @suppress{warning1|warning2}}.
*
* @param token The current token.
*/
private JsDocToken parseSuppressTag(JsDocToken token) {
if (token == JsDocToken.LC) {
Set<String> suppressions = new HashSet<String>();
while (true) {
if (match(JsDocToken.STRING)) {
suppressions.add(stream.getString());
token = next();
} else {
parser.addWarning("msg.jsdoc.suppress",
stream.getLineno(), stream.getCharno());
return token;
}
if (match(JsDocToken.PIPE)) {
token = next();
} else {
break;
}
}
if (!match(JsDocToken.RC)) {
parser.addWarning("msg.jsdoc.suppress",
stream.getLineno(), stream.getCharno());
} else {
token = next();
if (!jsdocBuilder.recordSuppressions(suppressions)) {
parser.addWarning("msg.jsdoc.suppress.duplicate",
stream.getLineno(), stream.getCharno());
}
}
}
return token;
}
/**
* Looks for a type expression at the current token and if found,
* returns it. Note that this method consumes input.
*
* @param token The current token.
* @return The type expression found or null if none.
*/
private Node parseAndRecordTypeNode(JsDocToken token) {
return parseAndRecordTypeNode(token, token == JsDocToken.LC);
}
/**
* Looks for
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> a type expression at the current token and if found,
* returns it. Note that this method consumes input.
*
* @param token The current token.
* @param matchingLC Whether the type expression starts with a "{".
* @return The type expression found or null if none.
*/
private Node parseAndRecordTypeNode(JsDocToken token, boolean matchingLC) {
return parseAndRecordTypeNode(token, stream.getLineno(), stream.getCharno(),
matchingLC, false);
}
/**
* Looks for a type expression at the current token and if found,
* returns it. Note that this method consumes input.
*
* @param token The current token.
* @param lineno The line of the type expression.
* @param startCharno The starting character position of the type expression.
* @return The type expression found or null if none.
*/
private Node parseAndRecordTypeNode(JsDocToken token, int lineno,
int startCharno) {
return parseAndRecordTypeNode(token, lineno, startCharno,
token == JsDocToken.LC, false);
}
/**
* Looks for a type expression at the current token and if found,
* returns it. Note that this method consumes input.
*
* @param token The current token.
* @param lineno The line of the type expression.
* @param startCharno The starting character position of the type expression.
* @param matchingLC Whether the type expression starts with a "{".
* @return The type expression found or null if none.
*/
private Node parseAndRecordTypeNameNode(JsDocToken token, int lineno,
int startCharno, boolean matchingLC) {
return parseAndRecordTypeNode(token, lineno, startCharno, matchingLC, true);
}
/**
* Looks for a type expression at the current token and if found,
* returns it. Note that this method consumes input.
*
* Parameter type expressions are special for two reasons:
* <ol>
* <li>They must begin with '{', to distinguish type names from param names.
* <li>They may end in '=', to denote optionality.
* </ol>
*
* @param token The current token.
* @return The type expression
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> found or null if none.
*/
private Node parseAndRecordParamTypeNode(JsDocToken token) {
Preconditions.checkArgument(token == JsDocToken.LC);
int lineno = stream.getLineno();
int startCharno = stream.getCharno();
Node typeNode = parseParamTypeExpressionAnnotation(token);
int endCharno = stream.getCharno();
jsdocBuilder.markTypeNode(typeNode, lineno, startCharno, endCharno,
true);
return typeNode;
}
/**
* Looks for a parameter type expression at the current token and if found,
* returns it. Note that this method consumes input.
*
* @param token The current token.
* @param lineno The line of the type expression.
* @param startCharno The starting character position of the type expression.
* @param matchingLC Whether the type expression starts with a "{".
* @param onlyParseSimpleNames If true, only simple type names are parsed
* (via a call to parseTypeNameAnnotation instead of
* parseTypeExpressionAnnotation).
* @return The type expression found or null if none.
*/
private Node parseAndRecordTypeNode(JsDocToken token, int lineno,
int startCharno,
boolean matchingLC,
boolean onlyParseSimpleNames) {
Node typeNode = null;
if (onlyParseSimpleNames) {
typeNode = parseTypeNameAnnotation(token);
} else {
typeNode = parseTypeExpressionAnnotation(token);
}
if (typeNode != null && !matchingLC) {
typeNode.putBooleanProp(Node.BRACELESS_TYPE, true);
}
int endCharno = stream.getCharno();
jsdocBuilder.markTypeNode(typeNode, lineno, startCharno, endCharno,
matchingLC);
return typeNode;
}
/**
* Determines whether the given type is a valid {@code @define} type.
*/
// TODO(nicksantos): Move this into a check pass.
private boolean isValidDefineType(Node typeNode) {
JSType type = typeRegistry.createFromTypeNodes(typeNode, "", null);
return !type.isUnknownType() && type.isSubtype(
typeRegistry.getNativeType(JSTypeNative.NUMBER_STRING_
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>BOOLEAN));
}
/**
* Converts a JSDoc token to its string representation.
*/
private String toString(JsDocToken token) {
switch (token) {
case ANNOTATION:
return "@" + stream.getString();
case BANG:
return "!";
case COMMA:
return ",";
case COLON:
return ":";
case GT:
return ">";
case LB:
return "[";
case LC:
return "{";
case LP:
return "(";
case LT:
return ".<";
case QMARK:
return "?";
case PIPE:
return "|";
case RB:
return "]";
case RC:
return "}";
case RP:
return ")";
case STAR:
return "*";
case ELLIPSIS:
return "...";
case EQUALS:
return "=";
case STRING:
return stream.getString();
default:
throw new IllegalStateException(token.toString());
}
}
/**
* Constructs a new {@code JSTypeExpression}.
* @param n A node. May be null.
*/
private JSTypeExpression createJSTypeExpression(Node n) {
return n == null ? null :
new JSTypeExpression(n, sourceName, typeRegistry);
}
/**
* Tuple for returning both the string extracted and the
* new token following a call to any of the extract*Block
* methods.
*/
private static class ExtractionInfo {
private final String string;
private final JsDocToken token;
public ExtractionInfo(String string, JsDocToken token) {
this.string = string;
this.token = token;
}
public String getString() {
return string;
}
public JsDocToken getToken() {
return token;
}
}
/**
* Extracts the text found on the current line starting at token. Note that
* token = token.info; should be called after this method is used to update
* the token properly in the parser.
*
* @return The extraction information.
*/
private ExtractionInfo extractSingleLineBlock() {
// Get the current starting point.
stream.update();
int lineno = stream.getLineno();
int charno = stream.getCharno() + 1;
String line = stream.get
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>RemainingJSDocLine().trim();
// Record the textual description.
if (line.length() > 0) {
jsdocBuilder.markText(line, lineno, charno, lineno,
charno + line.length());
}
return new ExtractionInfo(line, next());
}
private ExtractionInfo extractMultilineTextualBlock(JsDocToken token) {
return extractMultilineTextualBlock(token, WhitespaceOption.SINGLE_LINE);
}
private enum WhitespaceOption {
/**
* Preserves all whitespace and formatting. Needed for licenses and
* purposely formatted text.
*/
PRESERVE,
/** Preserves newlines but trims the output. */
TRIM,
/** Removes newlines and turns the output into a single line string. */
SINGLE_LINE
}
/**
* Extracts the text found on the current line and all subsequent
* until either an annotation, end of comment or end of file is reached.
* Note that if this method detects an end of line as the first token, it
* will quit immediately (indicating that there is no text where it was
* expected). Note that token = info.token; should be called after this
* method is used to update the token properly in the parser.
*
* @param token The start token.
* @param option How to handle whitespace.
*
* @return The extraction information.
*/
@SuppressWarnings("fallthrough")
private ExtractionInfo extractMultilineTextualBlock(JsDocToken token,
WhitespaceOption option) {
if (token == JsDocToken.EOC || token == JsDocToken.EOL ||
token == JsDocToken.EOF) {
return new ExtractionInfo("", token);
}
stream.update();
int startLineno = stream.getLineno();
int startCharno = stream.getCharno() + 1;
// Read the content from the first line.
String line = stream.getRemainingJSDocLine();
if (option != WhitespaceOption.PRESERVE) {
line = line.trim();
}
StringBuilder builder = new StringBuilder();
builder.append(line);
state = State.SEARCHING_ANNOTATION;
token = next();
boolean ignoreStar = false;
do {
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>
switch (token) {
case STAR:
if (!ignoreStar) {
if (builder.length() > 0) {
builder.append(line);
token = next();
}
} while (true);
}
/**
* Extracts the top-level block comment from the JsDoc comment, if any.
* This method differs from the extractMultilineTextualBlock in that it
* terminates under different conditions (it doesn't have the same
* prechecks), it does not first read in the remaining of the current
* line and its conditions for ignoring the "*" (STAR) are different.
*
* @param token The starting token.
*
* @return The extraction information.
*/
private ExtractionInfo extractBlockComment(JsDocToken token) {
StringBuilder builder = new StringBuilder();
boolean ignoreStar = true;
do {
switch (token) {
case ANNOTATION:
case EOC:
case EOF:
return new ExtractionInfo(builder.toString().trim(), token);
case STAR:
if (!ignoreStar) {
if (builder.length() > 0) {
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
ignoreStar = true;
builder.append('\n');
token = next();
continue;
default:
if (!ignoreStar && builder.length() > 0) {
builder.append(' ');
}
ignoreStar = false;
builder.append(toString(token));
String line = stream.getRemainingJSDocLine();
line = trimEnd(line);
builder.append(line);
token = next();
}
} while (true);
}
/**
* Trim characters from only the end of a string.
* This method will remove all whitespace characters
* (defined by Character.isWhitespace(char), in addition to the characters
* provided, from the end of the provided string.
*
* @param s String to be trimmed
* @return String with whitespace and characters in extraChars removed
* from the end.
*/
private static String trimEnd(String s) {
int trimCount = 0;
while (trimCount < s.length()) {
char ch = s.charAt(s.
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>length() - trimCount - 1);
if (Character.isWhitespace(ch)) {
trimCount++;
} else {
break;
}
}
if (trimCount == 0) {
return s;
}
return s.substring(0, s.length() - trimCount);
}
// Based on ES4 grammar proposed on July 10, 2008.
// http://wiki.ecmascript.org/doku.php?id=spec:spec
// Deliberately written to line up with the actual grammar rules,
// for maximum flexibility.
// TODO(nicksantos): The current implementation tries to maintain backwards
// compatibility with previous versions of the spec whenever we can.
// We should try to gradually withdraw support for these.
/**
* TypeExpressionAnnotation := TypeExpression |
* '{' TopLevelTypeExpression '}'
*/
private Node parseTypeExpressionAnnotation(JsDocToken token) {
if (token == JsDocToken.LC) {
skipEOLs();
Node typeNode = parseTopLevelTypeExpression(next());
if (typeNode != null) {
skipEOLs();
if (!match(JsDocToken.RC)) {
reportTypeSyntaxWarning("msg.jsdoc.missing.rc");
} else {
next();
}
}
return typeNode;
} else {
return parseTypeExpression(token);
}
}
/**
* ParamTypeExpressionAnnotation :=
* '{' OptionalParameterType '}' |
* '{' TopLevelTypeExpression '}' |
* '{' '...' TopLevelTypeExpression '}'
*
* OptionalParameterType :=
* TopLevelTypeExpression '='
*/
private Node parseParamTypeExpressionAnnotation(JsDocToken token) {
Preconditions.checkArgument(token == JsDocToken.LC);
skipEOLs();
boolean restArg = false;
token = next();
if (token == JsDocToken.ELLIPSIS) {
token = next();
if (token == JsDocToken.RC) {
// EMPTY represents the UNKNOWN type in the Type AST.
return wrapNode(Token.ELLIPSIS, new Node(Token.EMPTY));
}
restArg = true;
}
Node typeNode = parseTopLevelTypeExpression(token);
if (
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>typeNode != null) {
skipEOLs();
if (restArg) {
typeNode = wrapNode(Token.ELLIPSIS, typeNode);
} else if (match(JsDocToken.EQUALS)) {
next();
skipEOLs();
typeNode = wrapNode(Token.EQUALS, typeNode);
}
if (!match(JsDocToken.RC)) {
reportTypeSyntaxWarning("msg.jsdoc.missing.rc");
} else {
next();
}
}
return typeNode;
}
/**
* TypeNameAnnotation := TypeName | '{' TypeName '}'
*/
private Node parseTypeNameAnnotation(JsDocToken token) {
if (token == JsDocToken.LC) {
skipEOLs();
Node typeNode = parseTypeName(next());
if (typeNode != null) {
skipEOLs();
if (!match(JsDocToken.RC)) {
reportTypeSyntaxWarning("msg.jsdoc.missing.rc");
} else {
next();
}
}
return typeNode;
} else {
return parseTypeName(token);
}
}
/**
* TopLevelTypeExpression := TypeExpression
* | TypeUnionList
*
* We made this rule up, for the sake of backwards compatibility.
*/
private Node parseTopLevelTypeExpression(JsDocToken token) {
Node typeExpr = parseTypeExpression(token);
if (typeExpr != null) {
// top-level unions are allowed
if (match(JsDocToken.PIPE)) {
next();
if (match(JsDocToken.PIPE)) {
// We support double pipes for backwards-compatibility.
next();
}
skipEOLs();
token = next();
return parseUnionTypeWithAlternate(token, typeExpr);
}
}
return typeExpr;
}
/**
* TypeExpressionList := TopLevelTypeExpression
* | TopLevelTypeExpression ',' TypeExpressionList
*/
private Node parseTypeExpressionList(JsDocToken token) {
Node typeExpr = parseTopLevelTypeExpression(token);
if (typeExpr == null) {
return null;
}
Node typeList = new Node(Token.BLOCK);
typeList.addChildToBack(typeExpr);
while (match(JsDocToken.COMMA))
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> {
next();
skipEOLs();
typeExpr = parseTopLevelTypeExpression(next());
if (typeExpr == null) {
return null;
}
typeList.addChildToBack(typeExpr);
}
return typeList;
}
/**
* TypeExpression := BasicTypeExpression
* | '?' BasicTypeExpression
* | '!' BasicTypeExpression
* | BasicTypeExpression '?'
* | BasicTypeExpression '!'
*/
private Node parseTypeExpression(JsDocToken token) {
if (token == JsDocToken.QMARK) {
return wrapNode(Token.QMARK, parseBasicTypeExpression(next()));
} else if (token == JsDocToken.BANG) {
return wrapNode(Token.BANG, parseBasicTypeExpression(next()));
} else {
Node basicTypeExpr = parseBasicTypeExpression(token);
if (basicTypeExpr != null) {
if (match(JsDocToken.QMARK)) {
next();
return wrapNode(Token.QMARK, basicTypeExpr);
} else if (match(JsDocToken.BANG)) {
next();
return wrapNode(Token.BANG, basicTypeExpr);
}
}
return basicTypeExpr;
}
}
/**
* BasicTypeExpression := '*' | 'null' | 'undefined' | TypeName
* | FunctionType | UnionType | RecordType | ArrayType
*/
private Node parseBasicTypeExpression(JsDocToken token) {
if (token == JsDocToken.STAR) {
return newNode(Token.STAR);
} else if (token == JsDocToken.LB) {
skipEOLs();
return parseArrayType(next());
} else if (token == JsDocToken.LC) {
skipEOLs();
return parseRecordType(next());
} else if (token == JsDocToken.LP) {
skipEOLs();
return parseUnionType(next());
} else if (token == JsDocToken.STRING) {
String string = stream.getString();
if ("function".equals(string)) {
skipEOLs();
return parseFunctionType(next());
} else if ("null".equals(string) || "undefined".equals(string)) {
return newStringNode(string);
} else {
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> return parseTypeName(token);
}
}
return reportGenericTypeSyntaxWarning();
}
/**
* TypeName := NameExpression | NameExpression TypeApplication
* TypeApplication := '.<' TypeExpressionList '>'
* TypeExpressionList := TypeExpression // a white lie
*/
private Node parseTypeName(JsDocToken token) {
if (token != JsDocToken.STRING) {
return reportGenericTypeSyntaxWarning();
}
Node typeName = newStringNode(stream.getString());
if (match(JsDocToken.LT)) {
next();
skipEOLs();
Node memberType = parseTypeExpressionList(next());
if (memberType != null) {
typeName.addChildToFront(memberType);
skipEOLs();
if (!match(JsDocToken.GT)) {
return reportTypeSyntaxWarning("msg.jsdoc.missing.gt");
}
next();
}
}
return typeName;
}
/**
* FunctionType := 'function' FunctionSignatureType
* FunctionSignatureType :=
* TypeParameters '(' 'this' ':' TypeName, ParametersType ')' ResultType
*/
private Node parseFunctionType(JsDocToken token) {
// NOTE(nicksantos): We're not implementing generics at the moment, so
// just throw out TypeParameters.
if (token != JsDocToken.LP) {
return reportTypeSyntaxWarning("msg.jsdoc.missing.lp");
}
Node functionType = newNode(Token.FUNCTION);
Node parameters = null;
skipEOLs();
if (!match(JsDocToken.RP)) {
token = next();
boolean hasParams = true;
if (token == JsDocToken.STRING && "this".equals(stream.getString())) {
if (match(JsDocToken.COLON)) {
next();
skipEOLs();
Node thisType = wrapNode(Token.THIS, parseTypeName(next()));
if (thisType == null) {
return null;
}
functionType.addChildToFront(thisType);
} else {
return reportTypeSyntaxWarning("msg.jsdoc.missing.colon");
}
if (match(JsDocToken.COMMA)) {
next();
skipEOLs();
token = next();
} else {
hasParams = false;
}
}
if (hasParams
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>) {
parameters = parseParametersType(token);
if (parameters == null) {
return null;
}
}
}
if (parameters != null) {
functionType.addChildToBack(parameters);
}
skipEOLs();
if (!match(JsDocToken.RP)) {
return reportTypeSyntaxWarning("msg.jsdoc.missing.rp");
}
skipEOLs();
Node resultType = parseResultType(next());
if (resultType == null) {
return null;
} else {
functionType.addChildToBack(resultType);
}
return functionType;
}
/**
* ParametersType := RestParameterType | NonRestParametersType
* | NonRestParametersType ',' RestParameterType
* RestParameterType := '...' Identifier
* NonRestParametersType := ParameterType ',' NonRestParametersType
* | ParameterType
* | OptionalParametersType
* OptionalParametersType := OptionalParameterType
* | OptionalParameterType, OptionalParametersType
* OptionalParameterType := ParameterType=
* ParameterType := TypeExpression | Identifier ':' TypeExpression
*/
// NOTE(nicksantos): The official ES4 grammar forces optional and rest
// arguments to come after the required arguments. Our parser does not
// enforce this. Instead we allow them anywhere in the function at parse-time,
// and then warn about them during type resolution.
//
// In theory, it might be mathematically nicer to do the order-checking here.
// But in practice, the order-checking for structural functions is exactly
// the same as the order-checking for @param annotations. And the latter
// has to happen during type resolution. Rather than duplicate the
// order-checking in two places, we just do all of it in type resolution.
private Node parseParametersType(JsDocToken token) {
Node paramsType = newNode(Token.LP);
boolean isVarArgs = false;
Node paramType = null;
if (token != JsDocToken.RP) {
do {
if (paramType != null) {
// skip past the comma
next();
skipEOLs();
token = next();
}
if (token == JsDocToken.ELLIPSIS) {
// In the latest ES4 proposal, there are no type constraints allowed
// on variable arguments. We support the old
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> syntax for backwards
// compatibility, but we should gradually tear it out.
skipEOLs();
if (match(JsDocToken.RP)) {
paramType = newNode(Token.ELLIPSIS);
} else {
skipEOLs();
if (!match(JsDocToken.LB)) {
return reportTypeSyntaxWarning("msg.jsdoc.missing.lb");
}
next();
skipEOLs();
paramType = wrapNode(Token.ELLIPSIS, parseTypeExpression(next()));
skipEOLs();
if (!match(JsDocToken.RB)) {
return reportTypeSyntaxWarning("msg.jsdoc.missing.rb");
}
skipEOLs();
next();
}
isVarArgs = true;
} else {
paramType = parseTypeExpression(token);
if (match(JsDocToken.EQUALS)) {
skipEOLs();
next();
paramType = wrapNode(Token.EQUALS, paramType);
}
}
if (paramType == null) {
return null;
}
paramsType.addChildToBack(paramType);
if (isVarArgs) {
break;
}
} while (match(JsDocToken.COMMA));
}
if (isVarArgs && match(JsDocToken.COMMA)) {
return reportTypeSyntaxWarning("msg.jsdoc.function.varargs");
}
// The right paren will be checked by parseFunctionType
return paramsType;
}
/**
* ResultType := <empty> | ':' void | ':' TypeExpression
*/
private Node parseResultType(JsDocToken token) {
skipEOLs();
if (!match(JsDocToken.COLON)) {
return newNode(Token.EMPTY);
}
token = next();
skipEOLs();
if (match(JsDocToken.STRING) && "void".equals(stream.getString())) {
next();
return newNode(Token.VOID);
} else {
return parseTypeExpression(next());
}
}
/**
* UnionType := '(' TypeUnionList ')'
* TypeUnionList := TypeExpression | TypeExpression '|' TypeUnionList
*
* We've removed the empty union type.
*/
private Node parseUnionType(JsDocToken token) {
return parseUnionTypeWithAlternate(token
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>, null);
}
/**
* Create a new union type, with an alternate that has already been
* parsed. The alternate may be null.
*/
private Node parseUnionTypeWithAlternate(JsDocToken token, Node alternate) {
Node union = newNode(Token.PIPE);
if (alternate != null) {
union.addChildToBack(alternate);
}
Node expr = null;
do {
if (expr != null) {
skipEOLs();
token = next();
Preconditions.checkState(
token == JsDocToken.PIPE || token == JsDocToken.COMMA);
boolean isPipe = token == JsDocToken.PIPE;
if (isPipe && match(JsDocToken.PIPE)) {
// We support double pipes for backwards compatiblity.
next();
}
skipEOLs();
token = next();
}
expr = parseTypeExpression(token);
if (expr == null) {
return null;
}
union.addChildToBack(expr);
// We support commas for backwards compatiblity.
} while (match(JsDocToken.PIPE, JsDocToken.COMMA));
if (alternate == null) {
skipEOLs();
if (!match(JsDocToken.RP)) {
return reportTypeSyntaxWarning("msg.jsdoc.missing.rp");
}
next();
}
return union;
}
/**
* ArrayType := '[' ElementTypeList ']'
* ElementTypeList := <empty> | TypeExpression | '...' TypeExpression
* | TypeExpression ',' ElementTypeList
*/
private Node parseArrayType(JsDocToken token) {
Node array = newNode(Token.LB);
Node arg = null;
boolean hasVarArgs = false;
do {
if (arg != null) {
next();
skipEOLs();
token = next();
}
if (token == JsDocToken.ELLIPSIS) {
arg = wrapNode(Token.ELLIPSIS, parseTypeExpression(next()));
hasVarArgs = true;
} else {
arg = parseTypeExpression(token);
}
if (arg == null) {
return null;
}
array.addChildToBack(arg);
if (hasVarArgs) {
break;
}
skipEOLs();
} while (match(
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>JsDocToken.COMMA));
if (!match(JsDocToken.RB)) {
return reportTypeSyntaxWarning("msg.jsdoc.missing.rb");
}
next();
return array;
}
/**
* RecordType := '{' FieldTypeList '}'
*/
private Node parseRecordType(JsDocToken token) {
Node recordType = newNode(Token.LC);
Node fieldTypeList = parseFieldTypeList(token);
if (fieldTypeList == null) {
return reportGenericTypeSyntaxWarning();
}
skipEOLs();
if (!match(JsDocToken.RC)) {
return reportTypeSyntaxWarning("msg.jsdoc.missing.rc");
}
next();
recordType.addChildToBack(fieldTypeList);
return recordType;
}
/**
* FieldTypeList := FieldType | FieldType ',' FieldTypeList
*/
private Node parseFieldTypeList(JsDocToken token) {
Node fieldTypeList = newNode(Token.LB);
do {
Node fieldType = parseFieldType(token);
if (fieldType == null) {
return null;
}
fieldTypeList.addChildToBack(fieldType);
skipEOLs();
if (!match(JsDocToken.COMMA)) {
break;
}
// Move to the comma token.
next();
// Move to the token passed the comma.
skipEOLs();
token = next();
} while (true);
return fieldTypeList;
}
/**
* FieldType := FieldName | FieldName ':' TypeExpression
*/
private Node parseFieldType(JsDocToken token) {
Node fieldName = parseFieldName(token);
if (fieldName == null) {
return null;
}
skipEOLs();
if (!match(JsDocToken.COLON)) {
return fieldName;
}
// Move to the colon.
next();
// Move to the token after the colon and parse
// the type expression.
skipEOLs();
Node typeExpression = parseTypeExpression(next());
if (typeExpression == null) {
return null;
}
Node fieldType = newNode(Token.COLON);
fieldType.addChildToBack(fieldName);
fieldType.addChildToBack(typeExpression);
return fieldType;
}
/**
* FieldName := NameExpression | StringLiteral | NumberLiteral |
* ReservedIdentifier
*/
private Node parseFieldName(JsDoc
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>Token token) {
switch (token) {
case STRING:
String string = stream.getString();
return newStringNode(string);
default:
return null;
}
}
private Node wrapNode(int type, Node n) {
return n == null ? null :
new Node(type, n, stream.getLineno(), stream.getCharno());
}
private Node newNode(int type) {
return new Node(type, stream.getLineno(), stream.getCharno());
}
private Node newStringNode(String s) {
return Node.newString(s, stream.getLineno(), stream.getCharno());
}
private Node reportTypeSyntaxWarning(String warning) {
parser.addWarning(warning, stream.getLineno(), stream.getCharno());
return null;
}
private Node reportGenericTypeSyntaxWarning() {
return reportTypeSyntaxWarning("msg.jsdoc.type.syntax");
}
/**
* Eats tokens until {@link JsDocToken#EOL} included, and switches back the
* state to {@link State#SEARCHING_ANNOTATION}.
*/
private JsDocToken eatTokensUntilEOL() {
return eatTokensUntilEOL(next());
}
/**
* Eats tokens until {@link JsDocToken#EOL} included, and switches back the
* state to {@link State#SEARCHING_ANNOTATION}.
*/
private JsDocToken eatTokensUntilEOL(JsDocToken token) {
do {
if (token == JsDocToken.EOL || token == JsDocToken.EOC ||
token == JsDocToken.EOF) {
state = State.SEARCHING_ANNOTATION;
return token;
}
token = next();
} while (true);
}
/**
* Specific value indicating that the {@link #unreadToken} contains no token.
*/
private static final JsDocToken NO_UNREAD_TOKEN = null;
/**
* One token buffer.
*/
private JsDocToken unreadToken = NO_UNREAD_TOKEN;
/**
* Tests whether the next symbol of the token stream matches the specific
* token.
*/
private boolean match(JsDocToken token) {
unreadToken = next();
return unreadToken == token;
}
/**
* Tests that
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> the next symbol of the token stream matches one of the specified
* tokens.
*/
private boolean match(JsDocToken token1, JsDocToken token2) {
unreadToken = next();
return unreadToken == token1 || unreadToken == token2;
}
/**
* Gets the next token of the token stream or the buffered token if a matching
* was previously made.
*/
private JsDocToken next() {
if (unreadToken == NO_UNREAD_TOKEN) {
return stream.getJsDocToken();
} else {
return current();
}
}
/**
* Gets the current token, invalidating it in the process.
*/
private JsDocToken current() {
JsDocToken t = unreadToken;
unreadToken = NO_UNREAD_TOKEN;
return t;
}
/**
* Skips all EOLs and all empty lines in the JSDoc. Call this method if you
* want the JSDoc entry to span multiple lines.
*/
private void skipEOLs() {
while (match(JsDocToken.EOL)) {
next();
if (match(JsDocToken.STAR)) {
next();
}
}
}
/**
* Determines whether the parser has been populated with docinfo with a
* fileoverview tag.
*/
private boolean hasParsedFileOverviewDocInfo() {
return jsdocBuilder.isPopulatedWithFileOverview();
}
boolean hasParsedJSDocInfo() {
return jsdocBuilder.isPopulated();
}
JSDocInfo retrieveAndResetParsedJSDocInfo() {
return jsdocBuilder.build(sourceName);
}
/**
* Gets the fileoverview JSDocInfo, if any.
*/
JSDocInfo getFileOverviewJSDocInfo() {
return fileOverviewJSDocInfo;
}
}
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>
if (CombinedLiveRangeChecker.shouldVisit(n)) {
for (CombinedLiveRangeChecker callback : callbacks) {
callback.visit(t, n, parent);
}
}
}
}
/**
* A simple wrapper calls to call two AbstractCfgNodeTraversalCallback
* callback during the same traversal. Both traversals must have the same
* "shouldTraverse" conditions.
*/
private static class CombinedLiveRangeChecker
extends AbstractCfgNodeTraversalCallback {
private final LiveRangeChecker callback1;
private final LiveRangeChecker callback2;
CombinedLiveRangeChecker(
LiveRangeChecker callback1,
LiveRangeChecker callback2) {
this.callback1 = callback1;
this.callback2 = callback2;
}
/**
* @return Whether any CombinedLiveRangeChecker would be interested in the
* node.
*/
public static boolean shouldVisit(Node n) {
return LiveRangeChecker.shouldVisit(n);
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
callback1.visit(t, n, parent);
callback2.visit(t, n, parent);
}
void connectIfCrossed(UndiGraph<Var, Void> interferenceGraph) {
if (callback1.crossed || callback2.crossed) {
Var v1 = callback1.getDef();
Var v2 = callback2.getDef();
interferenceGraph.connectIfNotFound(v1, null, v2);
}
}
}
/**
* Tries to remove variable declaration if the variable has been coalesced
* with another variable that has already been declared.
*/
private void removeVarDeclaration(Node name) {
Node var = name.getParent();
Node parent = var.getParent();
// Special case when we are in FOR-IN loop.
if (NodeUtil.isForIn(parent)) {
var.removeChild(name);
parent.replaceChild(var, name);
} else if (var.getChildCount() == 1) {
// The removal is easy when there is only one variable in the VAR node.
if (name.hasChildren()) {
Node value = name.removeFirstChild();
var.removeChild(name);
Node assign = new Node(Token
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>.ASSIGN, name, value);
// We don't need to wrapped it with EXPR node if it is within a FOR.
if (parent.getType() != Token.FOR) {
assign = NodeUtil.newExpr(assign);
}
parent.replaceChild(var, assign);
} else {
// In a FOR( ; ; ) node, we must replace it with an EMPTY or else it
// becomes a FOR-IN node.
NodeUtil.removeChild(parent, var);
}
} else {
if (!name.hasChildren()) {
var.removeChild(name);
}
// We are going to leave duplicated declaration otherwise.
}
}
private static class LiveRangeChecker
extends AbstractCfgNodeTraversalCallback {
boolean defFound = false;
boolean crossed = false;
private final Var def;
private final Var use;
public LiveRangeChecker(Var def, Var use) {
this.def = def;
this.use = use;
}
Var getDef() {
return def;
}
/**
* @return Whether any LiveRangeChecker would be interested in the node.
*/
public static boolean shouldVisit(Node n) {
return (NodeUtil.isName(n)
|| (n.hasChildren() && NodeUtil.isName(n.getFirstChild())));
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
if (!defFound && isAssignTo(def, n, parent)) {
defFound = true;
}
if (defFound && (use == null || isReadFrom(use, n))) {
crossed = true;
}
}
private static boolean isAssignTo(Var var, Node n, Node parent) {
if (NodeUtil.isName(n) && var.getName().equals(n.getString()) &&
parent != null) {
if (parent.getType() == Token.LP) {
// In a function declaration, the formal parameters are assigned.
return true;
} else if (NodeUtil.isVar(parent)) {
// If this is a VAR declaration, if the name node has a child, we are
// assigning to that name.
return n.hasChildren();
}
return false; // Definitely a read.
} else {
// Last
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>ly, any assignmentOP is also an assign.
Node name = n.getFirstChild();
return name != null && NodeUtil.isName(name) &&
var.getName().equals(name.getString()) &&
NodeUtil.isAssignmentOp(n);
}
}
private static boolean isReadFrom(Var var, Node name) {
return name != null && NodeUtil.isName(name) &&
var.getName().equals(name.getString()) &&
!NodeUtil.isLhs(name, name.getParent());
}
}
}
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>
endLine();
}
@Override
void endCaseBody() {
super.endCaseBody();
indent--;
endStatement();
}
@Override
void appendOp(String op, boolean binOp) {
if (binOp) {
if (getLastChar() != ' ') {
append(" ");
}
append(op);
append(" ");
} else {
append(op);
}
}
/**
* If the body of a for loop or the then clause of an if statement has
* a single statement, should it be wrapped in a block?
* {@inheritDoc}
*/
@Override
boolean shouldPreserveExtraBlocks() {
// When pretty-printing, always place the statement in its own block
// so it is printed on a separate line. This allows breakpoints to be
// placed on the statement.
return true;
}
/**
* @return The TRY node for the specified CATCH node.
*/
private Node getTryForCatch(Node n) {
return n.getParent().getParent();
}
/**
* @return Whether the a line break should be added after the specified
* BLOCK.
*/
@Override
boolean breakAfterBlockFor(Node n, boolean isStatementContext) {
Preconditions.checkState(n.getType() == Token.BLOCK);
Node parent = n.getParent();
if (parent != null) {
int type = parent.getType();
switch (type) {
case Token.DO:
// Don't break before 'while' in DO-WHILE statements.
return false;
case Token.FUNCTION:
// FUNCTIONs are handled separately, don't break here.
return false;
case Token.TRY:
// Don't break before catch
return n != parent.getFirstChild();
case Token.CATCH:
// Don't break before finally
return !NodeUtil.hasFinally(getTryForCatch(parent));
case Token.IF:
// Don't break before else
return n == parent.getLastChild();
}
}
return true;
}
}
static class CompactCodePrinter
extends MappedCodePrinter
implements HasGetCode {
// The CompactCodePrinter tries to emit just enough newlines to stop there
// being lines longer than the threshold. Since the output is going to be
// gz
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>:
case Token.SHEQ:
case Token.SHNE:
case Token.CASE:
Node left;
Node right;
if (operatorToken == Token.CASE) {
left = condition.getParent().getFirstChild(); // the switch condition
right = condition.getFirstChild();
} else {
left = condition.getFirstChild();
right = condition.getLastChild();
}
Node typeOfNode = null;
Node stringNode = null;
if (left.getType() == Token.TYPEOF && right.getType() == Token.STRING) {
typeOfNode = left;
stringNode = right;
} else if (right.getType() == Token.TYPEOF &&
left.getType() == Token.STRING) {
typeOfNode = right;
stringNode = left;
}
if (typeOfNode != null && stringNode != null) {
Node operandNode = typeOfNode.getFirstChild();
JSType operandType = getTypeIfRefinable(operandNode, blindScope);
if (operandType != null) {
boolean resultEqualsValue = operatorToken == Token.EQ ||
operatorToken == Token.SHEQ || operatorToken == Token.CASE;
if (!outcome) {
resultEqualsValue = !resultEqualsValue;
}
return caseTypeOf(operandNode, operandType, stringNode.getString(),
resultEqualsValue, blindScope);
}
}
}
switch (operatorToken) {
case Token.AND:
if (outcome) {
return caseAndOrNotShortCircuiting(condition.getFirstChild(),
condition.getLastChild(), blindScope, true);
} else {
return caseAndOrMaybeShortCircuiting(condition.getFirstChild(),
condition.getLastChild(), blindScope, true);
}
case Token.OR:
if (!outcome) {
return caseAndOrNotShortCircuiting(condition.getFirstChild(),
condition.getLastChild(), blindScope, false);
} else {
return caseAndOrMaybeShortCircuiting(condition.getFirstChild(),
condition.getLastChild(), blindScope, false);
}
case Token.EQ:
if (outcome) {
return caseEquality(condition, blindScope, EQ);
} else {
return caseEquality(condition, blindScope, NE);
}
case Token.NE:
if (outcome) {
return caseEquality
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>(condition, blindScope, NE);
} else {
return caseEquality(condition, blindScope, EQ);
}
case Token.SHEQ:
if (outcome) {
return caseEquality(condition, blindScope, SHEQ);
} else {
return caseEquality(condition, blindScope, SHNE);
}
case Token.SHNE:
if (outcome) {
return caseEquality(condition, blindScope, SHNE);
} else {
return caseEquality(condition, blindScope, SHEQ);
}
case Token.NAME:
case Token.GETPROP:
return caseNameOrGetProp(condition, blindScope, outcome);
case Token.ASSIGN:
return firstPreciserScopeKnowingConditionOutcome(
condition.getFirstChild(),
firstPreciserScopeKnowingConditionOutcome(
condition.getFirstChild().getNext(), blindScope, outcome),
outcome);
case Token.NOT:
return firstPreciserScopeKnowingConditionOutcome(
condition.getFirstChild(), blindScope, !outcome);
case Token.LE:
case Token.LT:
case Token.GE:
case Token.GT:
if (outcome) {
return caseEquality(condition, blindScope, INEQ);
}
break;
case Token.INSTANCEOF:
return caseInstanceOf(
condition.getFirstChild(), condition.getLastChild(), blindScope,
outcome);
case Token.IN:
if (outcome && condition.getFirstChild().getType() == Token.STRING) {
return caseIn(condition.getLastChild(),
condition.getFirstChild().getString(), blindScope);
}
break;
case Token.CASE:
Node left =
condition.getParent().getFirstChild(); // the switch condition
Node right = condition.getFirstChild();
if (outcome) {
return caseEquality(left, right, blindScope, SHEQ);
} else {
return caseEquality(left, right, blindScope, SHNE);
}
}
return nextPreciserScopeKnowingConditionOutcome(
condition, blindScope, outcome);
}
private FlowScope caseEquality(Node condition, FlowScope blindScope,
Function<TypePair, TypePair> merging) {
return caseEquality(condition.getFirstChild(), condition.getLastChild(),
blindScope, merging);
}
private FlowScope caseEquality
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>compiler));
}
}
/**
* Covert EXPR_VOID to EXPR_RESULT to simplify the rest of the code.
*/
private void normalizeNodeTypes(Node n) {
if (n.getType() == Token.EXPR_VOID) {
n.setType(Token.EXPR_RESULT);
reportChange();
}
// Remove unused properties to minimize differences between ASTs
// produced by the two parsers.
if (n.getType() == Token.FUNCTION) {
Preconditions.checkState(n.getProp(Node.FUNCTION_PROP) == null);
}
normalizeBlocks(n);
for (Node child = n.getFirstChild();
child != null; child = child.getNext()) {
// This pass is run during the CompilerTestCase validation, so this
// parent pointer check serves as a more general check.
Preconditions.checkState(child.getParent() == n);
normalizeNodeTypes(child);
}
}
/**
* Add blocks to IF, WHILE, DO, etc.
*/
private void normalizeBlocks(Node n) {
if (NodeUtil.isControlStructure(n)
&& n.getType() != Token.LABEL
&& n.getType() != Token.SWITCH) {
for (Node c = n.getFirstChild(); c != null; c = c.getNext()) {
if (NodeUtil.isControlStructureCodeBlock(n,c) &&
c.getType() != Token.BLOCK) {
Node newBlock = new Node(Token.BLOCK, n.getLineno(), n.getCharno());
newBlock.copyInformationFrom(n);
n.replaceChild(c, newBlock);
if (c.getType() != Token.EMPTY) {
newBlock.addChildrenToFront(c);
} else {
newBlock.setWasEmptyNode(true);
}
c = newBlock;
reportChange();
}
}
}
}
/**
* Normalize where annotations appear on the AST. Copies
* around existing JSDoc annotations as well as internal annotations.
*/
static class PrepareAnnotations
extends NodeTraversal.AbstractPostOrderCallback {
private final CodingConvention convention;
PrepareAnnotations(AbstractCompiler compiler) {
this.convention = compiler.getCodingConvention();
}
/**
*
* In the AST that R
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>hino gives us, it needs to make a distinction
* between jsdoc on the object literal node and jsdoc on the object literal
* value. For example,
* <pre>
* var x = {
* / JSDOC /
* a: 'b',
* c: / JSDOC / 'd'
* };
* </pre>
*
* But in few narrow cases (in particular, function literals), it's
* a lot easier for us if the doc is attached to the value.
*/
public void visit(NodeTraversal t, Node n, Node parent) {
int nType = n.getType();
switch (nType) {
case Token.NAME:
case Token.STRING:
String nString = n.getString();
if (nType == Token.NAME &&
n.getParent().getType() == Token.CALL &&
"eval".equals(nString)) {
n.putBooleanProp(Node.DIRECT_EVAL, true);
}
if (convention.isConstant(nString)) {
n.putBooleanProp(Node.IS_CONSTANT_NAME, true);
}
break;
case Token.FUNCTION:
JSDocInfo fnInfo = n.getJSDocInfo();
if (fnInfo == null) {
// Look for the info on other nodes.
if (parent.getType() == Token.ASSIGN) {
// on ASSIGNs
fnInfo = parent.getJSDocInfo();
} else if (parent.getType() == Token.NAME) {
// on var NAME = function() { ... };
fnInfo = parent.getParent().getJSDocInfo();
}
}
// Compute which function parameters are optional and
// which are var_args.
Node args = n.getFirstChild().getNext();
for (Node arg = args.getFirstChild();
arg != null;
arg = arg.getNext()) {
String argName = arg.getString();
JSTypeExpression typeExpr = fnInfo == null ?
null : fnInfo.getParameterType(argName);
if (convention.isOptionalParameter(arg) ||
typeExpr != null && typeExpr.isOptionalArg()) {
arg.putBooleanProp(Node.IS_OPTIONAL_PARAM, true);
}
if (convention.isVarArgsParameter
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>(arg) ||
typeExpr != null && typeExpr.isVarArgs()) {
arg.putBooleanProp(Node.IS_VAR_ARGS_PARAM, true);
}
}
break;
case Token.OBJECTLIT:
if (n.getType() == Token.OBJECTLIT) {
for (Node key = n.getFirstChild();
key != null; key = key.getNext().getNext()) {
Node value = key.getNext();
if (key.getJSDocInfo() != null &&
key.getNext().getType() == Token.FUNCTION) {
value.setJSDocInfo(key.getJSDocInfo());
}
}
}
break;
}
// TODO(johnlenz): Determine if it is possible to simply use the javadoc
// everywhere rather than use IS_DISPATCHER.
/*
* Translate dispatcher info into the property expected node.
*/
if (n.getJSDocInfo() != null && n.getJSDocInfo().isJavaDispatch()) {
if (n.getType() == Token.ASSIGN) {
Node fnNode = n.getLastChild();
Preconditions.checkState(fnNode.getType() == Token.FUNCTION);
fnNode.putBooleanProp(Node.IS_DISPATCHER, true);
}
}
}
}
}
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> If the expectation is not met, issue a warning at the provided
* node's source code position.
*/
void expectStringOrNumber(
NodeTraversal t, Node n, JSType type, String msg) {
if (!type.matchesNumberContext() && !type.matchesStringContext()) {
mismatch(t, n, msg, type, NUMBER_STRING);
}
}
/**
* Expect the type to be anything but the void type. If the expectation is not
* met, issue a warning at the provided node's source code position. Note that
* a union type that includes the void type and at least one other type meets
* the expectation.
* @return Whether the expectation was met.
*/
boolean expectNotVoid(
NodeTraversal t, Node n, JSType type, String msg, JSType expectedType) {
if (type.isVoidType()) {
mismatch(t, n, msg, type, expectedType);
return false;
}
return true;
}
/**
* Expect that the type of a switch condition matches the type of its
* case condition.
*/
void expectSwitchMatchesCase(NodeTraversal t, Node n, JSType switchType,
JSType caseType) {
// ECMA-262, page 68, step 3 of evaluation of CaseBlock,
// but allowing extra autoboxing.
// TODO(user): remove extra conditions when type annotations
// in the code base have adapted to the change in the compiler.
if (!switchType.canTestForShallowEqualityWith(caseType) &&
(caseType.autoboxesTo() == null ||
!caseType.autoboxesTo().isSubtype(switchType))) {
mismatch(t, n.getFirstChild(),
"case expression doesn't match switch",
caseType, switchType);
}
}
/**
* Expect that the first type can be addressed with GETELEM syntax,
* and that the second type is the right type for an index into the
* first type.
*
* @param t The node traversal.
* @param n The node to issue warnings on.
* @param objType The type of the left side of the GETELEM.
* @param indexType The type inside the brackets of the GETELEM.
*/
void expectIndexMatch(NodeTraversal t
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> assigned to a symbol of the second
* type.
*
* @param t The node traversal.
* @param n The node to issue warnings on.
* @param rightType The type on the RHS of the assign.
* @param leftType The type of the symbol on the LHS of the assign.
* @param msg An extra message for the mismatch warning, if necessary.
* @return True if the types matched, false otherwise.
*/
boolean expectCanAssignTo(NodeTraversal t, Node n, JSType rightType,
JSType leftType, String msg) {
if (!rightType.canAssignTo(leftType)) {
if (bothIntrinsics(rightType, leftType)) {
// We have a superior warning for this mistake, which gives you
// the line numbers of both types.
registerMismatch(rightType, leftType);
} else {
mismatch(t, n, msg, rightType, leftType);
}
return false;
}
return true;
}
private boolean bothIntrinsics(JSType rightType, JSType leftType) {
return (leftType.isConstructor() || leftType.isEnumType()) &&
(rightType.isConstructor() || rightType.isEnumType());
}
/**
* Expect that the type of an argument matches the type of the parameter
* that it's fulfilling.
*
* @param t The node traversal.
* @param n The node to issue warnings on.
* @param argType The type of the argument.
* @param paramType The type of the parameter.
* @param callNode The call node, to help with the warning message.
* @param ordinal The argument ordinal, to help with the warning message.
*/
void expectArgumentMatchesParameter(NodeTraversal t, Node n, JSType argType,
JSType paramType, Node callNode, int ordinal) {
if (!argType.canAssignTo(paramType)) {
mismatch(t, n,
String.format("actual parameter %d of %s does not match " +
"formal parameter", ordinal,
getReadableJSTypeName(callNode.getFirstChild(), false)),
argType, paramType);
}
}
/**
* Expect that the first type can override a property of the second
* type.
*
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> native type.
if (var.input == null) {
n.setJSType(varType);
if (parent.getType() == Token.VAR) {
if (n.getFirstChild() != null) {
n.getFirstChild().setJSType(varType);
}
} else {
Preconditions.checkState(parent.getType() == Token.FUNCTION);
parent.setJSType(varType);
}
} else {
// Always warn about duplicates if the overridden type does not
// match the original type.
//
// If the types match, suppress the warning iff there was a @suppress
// tag, or if the original declaration was a stub.
if (!(allowDupe ||
var.getParentNode().getType() == Token.EXPR_RESULT) ||
!newType.equals(varType)) {
compiler.report(
JSError.make(sourceName, n, DUP_VAR_DECLARATION,
variableName, newType.toString(), var.getInputName(),
String.valueOf(var.nameNode.getLineno()),
varType.toString()));
}
}
}
}
/**
* Expect that all properties on interfaces that this type implements are
* implemented.
*/
void expectAllInterfacePropertiesImplemented(FunctionType type) {
ObjectType instance = type.getInstanceType();
for (ObjectType implemented : type.getAllImplementedInterfaces()) {
if (implemented.getImplicitPrototype() != null) {
for (String prop :
implemented.getImplicitPrototype().getOwnPropertyNames()) {
if (!instance.hasProperty(prop)) {
Node source = type.getSource();
Preconditions.checkNotNull(source);
String sourceName = (String) source.getProp(Node.SOURCENAME_PROP);
sourceName = sourceName == null ? "" : sourceName;
compiler.report(JSError.make(sourceName, source,
INTERFACE_METHOD_NOT_IMPLEMENTED,
prop, implemented.toString(), instance.toString()));
registerMismatch(instance, implemented);
}
}
}
}
}
/**
* Report a type mismatch
*/
private void mismatch(NodeTraversal t, Node n,
String msg, JSType found, JSType required) {
mismatch(t.getSourceName(), n, msg, found, required);
}
private void mismatch(Node
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>-readable
* name "Foo.prototype.bar".
*
* @param n The node.
* @param dereference If true, the type of the node will be dereferenced
* to an Object type, if possible.
*/
String getReadableJSTypeName(Node n, boolean dereference) {
// If we're analyzing a GETPROP, the property may be inherited by the
// prototype chain. So climb the prototype chain and find out where
// the property was originally defined.
if (n.getType() == Token.GETPROP) {
ObjectType objectType = getJSType(n.getFirstChild()).dereference();
if (objectType != null) {
String propName = n.getLastChild().getString();
while (objectType != null && !objectType.hasOwnProperty(propName)) {
objectType = objectType.getImplicitPrototype();
}
// Don't show complex function names or anonymous types.
// Instead, try to get a human-readable type name.
if (objectType != null &&
(objectType.getConstructor() != null ||
objectType.isFunctionPrototypeType())) {
return objectType.toString() + "." + propName;
}
}
}
JSType type = getJSType(n);
if (dereference) {
ObjectType dereferenced = type.dereference();
if (dereferenced != null) {
type = dereferenced;
}
}
String qualifiedName = n.getQualifiedName();
if (type.isFunctionPrototypeType() ||
(type.toObjectType() != null &&
type.toObjectType().getConstructor() != null)) {
return type.toString();
} else if (qualifiedName != null) {
return qualifiedName;
} else if (type instanceof FunctionType) {
// Don't show complex function names.
return "function";
} else {
return type.toString();
}
}
/**
* This method gets the JSType from the Node argument and verifies that it is
* present.
*/
private JSType getJSType(Node n) {
JSType jsType = n.getJSType();
if (jsType == null) {
// TODO(user): This branch indicates a compiler bug, not worthy of
// halting the compilation but we should log this and analyze to
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>/*
* Copyright 2009 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp.parsing;
import com.google.common.base.Preconditions;
import com.google.javascript.jscomp.mozilla.rhino.ScriptRuntime;
/**
* This class implements the scanner for JsDoc strings.
*
* It is heavily based on Rhino's TokenStream.
*
*
*/
class JsDocTokenStream {
/*
* For chars - because we need something out-of-range
* to check. (And checking EOF by exception is annoying.)
* Note distinction from EOF token type!
*/
private final static int
EOF_CHAR = -1;
JsDocTokenStream(String sourceString) {
this(sourceString, 0);
}
JsDocTokenStream(String sourceString, int lineno) {
this(sourceString, lineno, 0);
}
JsDocTokenStream(String sourceString, int lineno, int initCharno) {
Preconditions.checkNotNull(sourceString);
this.lineno = lineno;
this.sourceString = sourceString;
this.sourceEnd = sourceString.length();
this.sourceCursor = this.cursor = 0;
this.initLineno = lineno;
this.initCharno = initCharno;
}
/**
* Tokenizes JSDoc comments.
*/
@SuppressWarnings("fallthrough")
final JsDocToken getJsDocToken() {
int c;
stringBufferTop = 0;
for (;;) {
// eat white spaces
for (;;) {
charno = -1;
c = getChar();
if (c == EOF_CHAR) {
return
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> JsDocToken.EOF;
} else if (c == '\n') {
return JsDocToken.EOL;
} else if (!isJSSpace(c)) {
break;
}
}
switch (c) {
// annotation, e.g. @type or @constructor
case '@':
do {
c = getChar();
if (isAlpha(c)) {
addToString(c);
} else {
ungetChar(c);
this.string = getStringFromBuffer();
stringBufferTop = 0;
return JsDocToken.ANNOTATION;
}
} while (true);
case '*':
if (matchChar('/')) {
return JsDocToken.EOC;
} else {
return JsDocToken.STAR;
}
case ',':
return JsDocToken.COMMA;
case '>':
return JsDocToken.GT;
case '(':
return JsDocToken.LP;
case ')':
return JsDocToken.RP;
case '{':
return JsDocToken.LC;
case '}':
return JsDocToken.RC;
case '[':
return JsDocToken.LB;
case ']':
return JsDocToken.RB;
case '?':
return JsDocToken.QMARK;
case '!':
return JsDocToken.BANG;
case ':':
return JsDocToken.COLON;
case '=':
return JsDocToken.EQUALS;
case '|':
matchChar('|');
return JsDocToken.PIPE;
case '.':
c = getChar();
if (c == '<') {
return JsDocToken.LT;
} else {
if (c == '.') {
c = getChar();
if (c == '.') {
return JsDocToken.ELLIPSIS;
} else {
addToString('.');
}
}
// we may backtrack across line boundary
ungetBuffer[ungetCursor++] = c;
c = '.';
}
// fall through
default: {
// recognize a jsdoc string but discard last . if it is followed by
// a non-jsdoc comment char, e.g. Array.<
int c1 = c;
addToString(c);
int c2 = getChar();
if (!isJSDocString(
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>c2)) {
ungetChar(c2);
this.string = getStringFromBuffer();
stringBufferTop = 0;
return JsDocToken.STRING;
} else {
do {
c1 = c2;
c2 = getChar();
if (c1 == '.' && c2 == '<') {
ungetChar(c2);
ungetChar(c1);
this.string = getStringFromBuffer();
stringBufferTop = 0;
return JsDocToken.STRING;
} else {
if (isJSDocString(c2)) {
addToString(c1);
} else {
ungetChar(c2);
addToString(c1);
this.string = getStringFromBuffer();
stringBufferTop = 0;
return JsDocToken.STRING;
}
}
} while (true);
}
}
}
}
}
/**
* Gets the remaining JSDoc line without the {@link JsDocToken#EOL},
* {@link JsDocToken#EOF} or {@link JsDocToken#EOC}.
*/
@SuppressWarnings("fallthrough")
String getRemainingJSDocLine() {
int c;
for (;;) {
c = getChar();
switch (c) {
case '*':
if (peekChar() != '/') {
addToString(c);
break;
}
// fall through
case EOF_CHAR:
case '\n':
ungetChar(c);
this.string = getStringFromBuffer();
stringBufferTop = 0;
return this.string;
default:
addToString(c);
break;
}
}
}
final int getLineno() { return lineno; }
final int getCharno() {
return lineno == initLineno? initCharno + charno : charno;
}
final String getString() { return string; }
final boolean eof() { return hitEOF; }
private String getStringFromBuffer() {
tokenEnd = cursor;
return new String(stringBuffer, 0, stringBufferTop);
}
private void addToString(int c) {
int N = stringBufferTop;
if (N == stringBuffer.length) {
char[] tmp = new char[stringBuffer.length * 2];
System.arraycopy
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>Later == n) {
lvalueToRemoveLater = null;
if (n.getType() == Token.ASSIGN) {
Node last = n.getLastChild();
n.removeChild(last);
parent.replaceChild(n, last);
} else {
Preconditions.checkState(n.getType() == Token.NAME);
n.removeChild(n.getFirstChild());
}
compiler.reportCodeChange();
}
if (n.getType() == Token.CALL) {
if (t.inGlobalScope()) {
// If there's a function call in the global scope,
// we just say it's unsafe and freeze all the defines.
//
// NOTE(nicksantos): We could be a lot smarter here. For example,
// ReplaceOverriddenVars keeps a call graph of all functions and
// which functions/variables that they reference, and tries
// to statically determine which functions are "safe" and which
// are not. But this would be overkill, expecially because
// the intended use of defines is with config_files, where
// all the defines are at the top of the bundle.
for (DefineInfo info : assignableDefines.values()) {
setDefineInfoNotAssignable(info, t);
}
assignableDefines.clear();
}
}
updateAssignAllowedStack(n, false);
}
/**
* Determines whether assignment to a define should be allowed
* in the subtree of the given node, and if not, records that fact.
*
* @param n The node whose subtree we're about to enter or exit.
* @param entering True if we're entering the subtree, false otherwise.
*/
private void updateAssignAllowedStack(Node n, boolean entering) {
switch (n.getType()) {
case Token.CASE:
case Token.FOR:
case Token.FUNCTION:
case Token.HOOK:
case Token.IF:
case Token.SWITCH:
case Token.WHILE:
if (entering) {
assignAllowed.push(0);
} else {
assignAllowed.remove();
}
break;
}
}
/**
* Determines whether assignment to a define should be allowed
* at the current point of the traversal.
*/
private boolean isAssignAllowed() {
return assignAllowed.element() == 1;
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> visit all nodes but not traverse into function
* bodies.
*/
public abstract static class AbstractShallowCallback implements Callback {
public final boolean shouldTraverse(NodeTraversal nodeTraversal, Node n,
Node parent) {
// We do want to traverse the name of a named function, but we don't
// want to traverse the arguments or body.
return parent == null || parent.getType() != Token.FUNCTION ||
n == parent.getFirstChild();
}
}
/**
* Abstract callback to visit all structure and statement nodes but doesn't
* traverse into functions or expressions.
*/
public abstract static class AbstractShallowStatementCallback
implements Callback {
public final boolean shouldTraverse(NodeTraversal nodeTraversal, Node n,
Node parent) {
return parent == null || NodeUtil.isControlStructure(parent)
|| NodeUtil.isStatementBlock(parent);
}
}
/**
* Abstract callback to visit a pruned set of nodes.
*
*/
public abstract static class AbstractNodeTypePruningCallback
implements Callback {
private final Set<Integer> nodeTypes;
private final boolean include;
/**
* Creates an abstract pruned callback.
* @param nodeTypes the nodes to include in the traversal
*/
public AbstractNodeTypePruningCallback(Set<Integer> nodeTypes) {
this(nodeTypes, true);
}
/**
* Creates an abstract pruned callback.
* @param nodeTypes the nodes to include/exclude in the traversal
* @param include whether to include or exclude the nodes in the traversal
*/
public AbstractNodeTypePruningCallback(Set<Integer> nodeTypes,
boolean include) {
this.nodeTypes = nodeTypes;
this.include = include;
}
public boolean shouldTraverse(NodeTraversal nodeTraversal, Node n,
Node parent) {
return include == nodeTypes.contains(n.getType());
}
}
/**
* Creates a node traversal using the specified callback interface.
*/
public NodeTraversal(AbstractCompiler compiler, Callback cb) {
this(compiler, cb, new SyntacticScopeCreator(compiler));
}
/**
* Creates a node traversal using the specified callback interface
* and the scope creator.
*/
public NodeTraversal(AbstractCompiler compiler, Callback cb,
ScopeCreator scopeCreator) {
this.callback = cb;
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>);
}
}
private static final String MISSING_SOURCE = "[source unknown]";
private String formatNodePosition(Node n) {
if (n == null) {
return MISSING_SOURCE + "\n";
}
int lineNumber = n.getLineno();
int columnNumber = n.getCharno();
String src = compiler.getSourceLine(sourceName, lineNumber);
if (src == null) {
src = MISSING_SOURCE;
}
return sourceName + ":" + lineNumber + ":" + columnNumber + "\n"
+ src + "\n";
}
/**
* Traverses a parse tree recursively with a scope, starting with the given
* root. This should only be used in the global scope. Otherwise, use
* {@link #traverseAtScope}.
*/
void traverseWithScope(Node root, Scope s) {
Preconditions.checkState(s.isGlobal());
sourceName = "";
curNode = root;
pushScope(s);
traverseBranch(root, null);
popScope();
}
/**
* Traverses a parse tree recursively with a scope, starting at that scope's
* root.
*/
void traverseAtScope(Scope s) {
Node n = s.getRootNode();
if (n.getType() == Token.FUNCTION) {
// We need to do some extra magic to make sure that the scope doesn't
// get re-created when we dive into the function.
sourceName = getSourceName(n);
curNode = n;
pushScope(s);
Node args = n.getFirstChild().getNext();
Node body = args.getNext();
traverseBranch(args, n);
traverseBranch(body, n);
popScope();
} else {
traverseWithScope(n, s);
}
}
/**
* Traverses an inner node recursively with a refined scope. An inner node may
* be any node with a non {@code null} parent (i.e. all nodes except the
* root).
*
* @param node the node to traverse
* @param parent the node's parent, it may be not be {@code null}
* @param refinedScope the refined scope of the scope currently at the top of
* the scope stack or in trivial cases that very scope or {@
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>fallthrough")
private void traverseBranch(Node n, Node parent) {
int type = n.getType();
if (type == Token.SCRIPT) {
sourceName = getSourceName(n);
}
curNode = n;
if (!callback.shouldTraverse(this, n, parent)) return;
switch (type) {
case Token.CATCH:
Preconditions.checkState(n.getChildCount() == 3);
Preconditions.checkState(n.getFirstChild().getType() == Token.NAME);
// the first child is the catch var and the third child
// is the code block
traverseBranch(n.getFirstChild(), n);
traverseBranch(n.getFirstChild().getNext().getNext(), n);
break;
case Token.FUNCTION:
traverseFunction(n, parent);
break;
default:
for (Node child = n.getFirstChild(); child != null; ) {
// child could be replaced, in which case our child node
// would no longer point to the true next
Node next = child.getNext();
traverseBranch(child, n);
child = next;
}
break;
}
curNode = n;
callback.visit(this, n, parent);
}
/**
* Traverses a function.
*/
private void traverseFunction(Node n, Node parent) {
Preconditions.checkState(n.getChildCount() == 3);
Preconditions.checkState(n.getType() == Token.FUNCTION);
final Node fnName = n.getFirstChild();
boolean anonymous = parent != null && NodeUtil.isFunctionAnonymous(n);
if (!anonymous) {
// Named functions are parent of the containing scope.
traverseBranch(fnName, n);
}
curNode = n;
pushScope(n);
if (anonymous) {
// Anonymous function names are parent of the contained scope.
traverseBranch(fnName, n);
}
final Node args = fnName.getNext();
final Node body = args.getNext();
// Args
traverseBranch(args, n);
// Body
Preconditions.checkState(body.getNext() == null &&
body.getType() == Token.BLOCK);
traverseBranch(body, n);
popScope();
}
/** Examines the functions stack for the last instance of a function node. */
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> @SuppressWarnings("unchecked")
public Node getEnclosingFunction() {
if (scopes.size() + scopeRoots.size() < 2) {
return null;
} else {
if (scopeRoots.isEmpty()) {
return scopes.peek().getRootNode();
} else {
return scopeRoots.peek();
}
}
}
/** Creates a new scope (e.g. when entering a function). */
private void pushScope(Node node) {
Preconditions.checkState(curNode != null);
scopeRoots.push(node);
cfgs.push(null);
if (scopeCallback != null) {
scopeCallback.enterScope(this);
}
}
/** Creates a new scope (e.g. when entering a function). */
private void pushScope(Scope s) {
Preconditions.checkState(curNode != null);
scopes.push(s);
cfgs.push(null);
if (scopeCallback != null) {
scopeCallback.enterScope(this);
}
}
/** Pops back to the previous scope (e.g. when leaving a function). */
private void popScope() {
if (scopeCallback != null) {
scopeCallback.exitScope(this);
}
if (scopeRoots.isEmpty()) {
scopes.pop();
} else {
scopeRoots.pop();
}
cfgs.pop();
}
/** Gets the current scope. */
public Scope getScope() {
Scope scope = scopes.isEmpty() ? null : scopes.peek();
if (scopeRoots.isEmpty()) {
return scope;
}
Iterator<Node> it = scopeRoots.descendingIterator();
while (it.hasNext()) {
scope = scopeCreator.createScope(it.next(), scope);
scopes.push(scope);
}
scopeRoots.clear();
return scope;
}
/** Gets the control flow graph for the current JS scope. */
public ControlFlowGraph<Node> getControlFlowGraph() {
if (cfgs.peek() == null) {
ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false);
cfa.process(null, getScopeRoot());
cfgs.pop();
cfgs.push(cfa.getCfg());
}
return cfgs.peek();
}
/** Returns
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> the current scope's root. */
public Node getScopeRoot() {
if (scopeRoots.isEmpty()) {
return scopes.peek().getRootNode();
} else {
return scopeRoots.peek();
}
}
/**
* Determines whether the traversal is currently in the global scope.
*/
boolean inGlobalScope() {
return getScopeDepth() <= 1;
}
int getScopeDepth() {
return scopes.size() + scopeRoots.size();
}
public boolean hasScope() {
return !(scopes.isEmpty() && scopeRoots.isEmpty());
}
/** Reports a diagnostic (error or warning) */
public void report(Node n, DiagnosticType diagnosticType,
String... arguments) {
JSError error = JSError.make(getSourceName(), n, diagnosticType, arguments);
compiler.report(error);
}
private static String getSourceName(Node n) {
String name = (String) n.getProp(Node.SOURCENAME_PROP);
return name == null ? "" : name;
}
/**
* Creates a JSError during NodeTraversal.
*
* @param n Determines the line and char position within the source file name
* @param type The DiagnosticType
* @param arguments Arguments to be incorporated into the message
*/
public JSError makeError(Node n, CheckLevel level, DiagnosticType type,
String... arguments) {
return JSError.make(getSourceName(), n.getLineno(), n.getCharno(), level,
type, arguments);
}
/**
* Creates a JSError during NodeTraversal.
*
* @param n Determines the line and char position within the source file name
* @param type The DiagnosticType
* @param arguments Arguments to be incorporated into the message
*/
public JSError makeError(Node n, DiagnosticType type, String... arguments) {
return JSError.make(getSourceName(), n, type, arguments);
}
}
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>/*
* Copyright 2009 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp.parsing;
/**
* JSDoc-specific tokens.
*
* This class is based on Rhino's Token.
*
*
*/
enum JsDocToken {
// Tokens recycled from Rhino
EOF, // end of file token - (not EOF_CHAR)
EOL, // end of line
LT,
GT,
STRING,
LB, // left and right brackets
RB,
LC, // left and right curlies (braces)
RC,
LP, // left and right parentheses
RP,
COMMA, // comma operator
COLON,
// JsDoc-only tokens
ANNOTATION,
PIPE,
STAR,
EOC,
QMARK,
ELLIPSIS,
BANG,
EQUALS;
}
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>modules.length];
for (int i = 0; i < modules.length; i++) {
expected[i] = "";
for (CompilerInput input : modules[i].getInputs()) {
expected[i] += input.getSourceFile().getCode();
}
}
test(modules, expected, null, warning);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* Verifies that the compiler pass's JS output matches the expected output
* and (optionally) that an expected warning is issued. Or, if an error is
* expected, this method just verifies that the error is encountered.
*
* @param compiler A compiler that has been initialized via
* {@link Compiler#init}
* @param expected Expected output, or null if an error is expected
* @param error Expected error, or null if no error is expected
* @param warning Expected warning, or null if no warning is expected
*/
protected void test(Compiler compiler, String[] expected,
DiagnosticType error, DiagnosticType warning) {
test(compiler, expected, error, warning, null);
}
/**
* Verifies that the compiler pass's JS output matches the expected output
* and (optionally) that an expected warning is issued. Or, if an error is
* expected, this method just verifies that the error is encountered.
*
* @param compiler A compiler that has been initialized via
* {@link Compiler#init}
* @param expected Expected output, or null if an error is expected
* @param error Expected error, or null if no error is expected
* @param warning Expected warning, or null if no warning is expected
* @param description The description of the expected warning,
* or null if no warning is expected or if the warning's description
* should not be examined
*/
private void test(Compiler compiler, String[] expected,
DiagnosticType error, DiagnosticType warning,
String description) {
RecentChange recentChange = new RecentChange();
compiler.addChangeHandler(recentChange);
Node root = compiler.parseInputs();
assertTrue("Unexpected parse error(s): " +
Joiner.on("\n").join(compiler.getErrors()), root != null);
Node externsRoot = root.getFirstChild();
Node mainRoot = root.getLastChild
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>();
// Save the tree for later comparison.
Node rootClone = root.cloneTree();
Node externsRootClone = rootClone.getFirstChild();
Node mainRootClone = rootClone.getLastChild();
int numRepetitions = getNumRepetitions();
ErrorManager[] errorManagers = new ErrorManager[numRepetitions];
int aggregateWarningCount = 0;
List<JSError> aggregateWarnings = Lists.newArrayList();
boolean hasCodeChanged = false;
assertFalse("Code should not change before processing",
recentChange.hasCodeChanged());
for (int i = 0; i < numRepetitions; ++i) {
if (compiler.getErrorCount() == 0) {
errorManagers[i] = new BlackHoleErrorManager(compiler);
// Only run the type checking pass once, if asked.
// Running it twice can cause unpredictable behavior because duplicate
// objects for the same type are created, and the type system
// uses reference equality to compare many types.
if (typeCheckEnabled && i == 0) {
TypeCheck check = createTypeCheck(compiler, typeCheckLevel);
check.processForTesting(externsRoot, mainRoot);
}
// Only run the normalize pass once, if asked.
if (normalizeEnabled && i == 0) {
Normalize normalize = new Normalize(compiler, false);
normalize.process(externsRoot, mainRoot);
compiler.setNormalized();
}
if (markNoSideEffects && i == 0) {
MarkNoSideEffectCalls mark = new MarkNoSideEffectCalls(compiler);
mark.process(externsRoot, mainRoot);
}
recentChange.reset();
getProcessor(compiler).process(externsRoot, mainRoot);
if (checkLineNumbers) {
(new LineNumberCheck(compiler)).process(externsRoot, mainRoot);
}
hasCodeChanged = hasCodeChanged || recentChange.hasCodeChanged();
aggregateWarningCount += errorManagers[i].getWarningCount();
aggregateWarnings.addAll(Lists.newArrayList(compiler.getWarnings()));
if (normalizeEnabled) {
boolean verifyDeclaredConstants = true;
new Normalize.VerifyConstants(compiler, verifyDeclaredConstants)
.process(externsRoot, mainRoot);
}
}
}
if (error == null) {
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>
"compiler.reportCodeChange() was called " +
"even though nothing changed",
hasCodeChanged);
} else {
assertTrue("compiler.reportCodeChange() should have been called",
hasCodeChanged);
}
if (compareAsTree) {
String explanation = expectedRoot.checkTreeEquals(mainRoot);
assertNull("\nExpected: " + compiler.toSource(expectedRoot) +
"\nResult: " + compiler.toSource(mainRoot) +
"\n" + explanation, explanation);
} else if (expected != null) {
assertEquals(
Joiner.on("").join(expected), compiler.toSource(mainRoot));
}
// Verify normalization is not invalidated.
Node normalizeCheckRootClone = root.cloneTree();
Node normalizeCheckExternsRootClone = root.getFirstChild();
Node normalizeCheckMainRootClone = root.getLastChild();
new PrepareAst(compiler).process(
normalizeCheckExternsRootClone, normalizeCheckMainRootClone);
String explanation =
normalizeCheckMainRootClone.checkTreeEquals(mainRoot);
assertNull("Node structure normalization invalidated.\nExpected: " +
compiler.toSource(normalizeCheckMainRootClone) +
"\nResult: " + compiler.toSource(mainRoot) +
"\n" + explanation, explanation);
// TODO(johnlenz): enable this for most test cases.
// Currently, this invalidates test for while-loops, for-loop
// initializers, and other naming. However, a set of code
// (FoldConstants, etc) runs before the Normalize pass, so this can't be
// force on everywhere.
if (normalizeEnabled) {
new Normalize(compiler, true).process(
normalizeCheckExternsRootClone, normalizeCheckMainRootClone);
explanation = normalizeCheckMainRootClone.checkTreeEquals(mainRoot);
assertNull("Normalization invalidated.\nExpected: " +
compiler.toSource(normalizeCheckMainRootClone) +
"\nResult: " + compiler.toSource(mainRoot) +
"\n" + explanation, explanation);
}
} else {
String errors = "";
for (JSError actualError : compiler.getErrors()) {
errors += actualError.description + "\n";
}
assertEquals("There should be one error. "
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> + errors,
1, compiler.getErrorCount());
assertEquals(errors, error, compiler.getErrors()[0].getType());
}
}
/**
* Parses expected js inputs and returns the root of the parse tree.
*/
private Node parseExpectedJs(String[] expected) {
Compiler compiler = createCompiler();
JSSourceFile[] inputs = new JSSourceFile[expected.length];
for (int i = 0; i < expected.length; i++) {
inputs[i] = JSSourceFile.fromCode("expected" + i, expected[i]);
}
compiler.init(externsInputs, inputs, getOptions());
Node root = compiler.parseInputs();
assertTrue("Unexpected parse error(s): " +
Joiner.on("\n").join(compiler.getErrors()), root != null);
Node externsRoot = root.getFirstChild();
Node mainRoot = externsRoot.getNext();
// Only run the normalize pass, if asked.
if (normalizeEnabled && normalizeExpected && !compiler.hasErrors()) {
Normalize normalize = new Normalize(compiler, false);
normalize.process(externsRoot, mainRoot);
compiler.setNormalized();
}
return mainRoot;
}
Node parseExpectedJs(String expected) {
return parseExpectedJs(new String[] {expected});
}
/**
* Generates a list of modules from a list of inputs, such that each module
* depends on the module before it.
*/
static JSModule[] createModuleChain(String... inputs) {
JSModule[] modules = createModules(inputs);
for (int i = 1; i < modules.length; i++) {
modules[i].addDependency(modules[i - 1]);
}
return modules;
}
/**
* Generates a list of modules from a list of inputs, such that each module
* depends on the first module.
*/
static JSModule[] createModuleStar(String... inputs) {
JSModule[] modules = createModules(inputs);
for (int i = 1; i < modules.length; i++) {
modules[i].addDependency(modules[0]);
}
return modules;
}
/**
* Generates a list of modules from a list of inputs. Does not generate any
* dependencies between the modules.
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> case Token.WHILE:
case Token.DO:
case Token.IF:
computeGenKill(NodeUtil.getConditionExpression(n), gen, kill,
conditional);
return;
case Token.FOR:
if (!NodeUtil.isForIn(n)) {
computeGenKill(NodeUtil.getConditionExpression(n), gen, kill,
conditional);
} else {
// for(x in y) {...}
Node lhs = n.getFirstChild();
Node rhs = lhs.getNext();
if (NodeUtil.isVar(lhs)) {
// for(var x in y) {...}
lhs = lhs.getLastChild();
}
addToSetIfLocal(lhs, kill);
addToSetIfLocal(lhs, gen);
computeGenKill(rhs, gen, kill, conditional);
}
return;
case Token.VAR:
for (Node c = n.getFirstChild(); c != null; c = c.getNext()) {
if (c.hasChildren()) {
computeGenKill(c.getFirstChild(), gen, kill, conditional);
if (!conditional) {
addToSetIfLocal(c, kill);
}
}
}
return;
case Token.AND:
case Token.OR:
computeGenKill(n.getFirstChild(), gen, kill, conditional);
// May short circuit.
computeGenKill(n.getLastChild(), gen, kill, true);
return;
case Token.HOOK:
computeGenKill(n.getFirstChild(), gen, kill, conditional);
// Assume both sides are conditional.
computeGenKill(n.getFirstChild().getNext(), gen, kill, true);
computeGenKill(n.getLastChild(), gen, kill, true);
return;
case Token.NAME:
if (isArgumentsName(n)) {
markAllParametersEscaped();
} else {
addToSetIfLocal(n, gen);
}
return;
default:
if (NodeUtil.isAssignmentOp(n) && NodeUtil.isName(n.getFirstChild())) {
Node lhs = n.getFirstChild();
if (!conditional) {
addToSetIfLocal(lhs, kill);
}
if (!NodeUtil.isAssign(n)) {
// assignments such as a += 1 reads a.
addToSetIfLocal(
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>lhs, gen);
}
computeGenKill(lhs.getNext(), gen, kill, conditional);
} else {
for (Node c = n.getFirstChild(); c != null; c = c.getNext()) {
computeGenKill(c, gen, kill, conditional);
}
}
return;
}
}
private void addToSetIfLocal(Node node, BitSet set) {
Preconditions.checkState(NodeUtil.isName(node));
String name = node.getString();
if (!jsScope.isDeclared(name, false)) {
return;
}
Var var = jsScope.getVar(name);
if (!escaped.contains(var)) {
set.set(var.index);
}
}
/**
* Give up computing liveness of formal parameter by putting all the parameter
* names in the escaped set.
*/
void markAllParametersEscaped() {
Node lp = jsScope.getRootNode().getFirstChild().getNext();
for(Node arg = lp.getFirstChild(); arg != null; arg = arg.getNext()) {
escaped.add(jsScope.getVar(arg.getString()));
}
}
private boolean isArgumentsName(Node n) {
if (n.getType() != Token.NAME ||
!n.getString().equals(ARGUMENT_ARRAY_ALIAS) ||
jsScope.isDeclared(ARGUMENT_ARRAY_ALIAS, false)) {
return false;
} else {
return true;
}
}
}
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> != null) {
check(externsRoot, true);
}
check(jsRoot, false);
potentialChecks.flush();
}
/** Main entry point of this phase for testing code. */
public Scope processForTesting(Node externsRoot, Node jsRoot) {
Preconditions.checkState(scopeCreator == null);
Preconditions.checkState(topScope == null);
Preconditions.checkState(jsRoot.getParent() != null);
Node externsAndJsRoot = jsRoot.getParent();
scopeCreator = new MemoizedScopeCreator(new TypedScopeCreator(compiler));
topScope = scopeCreator.createScope(externsAndJsRoot, null);
TypeInferencePass inference = new TypeInferencePass(compiler,
reverseInterpreter, topScope, scopeCreator);
inference.process(externsRoot, jsRoot);
process(externsRoot, jsRoot);
return topScope;
}
public void check(Node node, boolean externs) {
Preconditions.checkNotNull(node);
NodeTraversal t = new NodeTraversal(compiler, this, scopeCreator);
inExterns = externs;
t.traverseWithScope(node, topScope);
if (externs) {
inferJSDocInfo.process(node, null);
} else {
inferJSDocInfo.process(null, node);
}
}
public boolean shouldTraverse(
NodeTraversal t, Node n, Node parent) {
JSDocInfo info;
switch (n.getType()) {
case Token.SCRIPT:
case Token.VAR:
// @notypecheck
info = n.getJSDocInfo();
if (info != null && info.isNoTypeCheck()) {
return false;
}
break;
case Token.FUNCTION:
// @notypecheck
info = n.getJSDocInfo();
info = (info == null) ? parent.getJSDocInfo() : info;
if (info != null && info.isNoTypeCheck()) {
return false;
}
// normal type checking
final TypeCheck outerThis = this;
final Scope outerScope = t.getScope();
final FunctionType functionType = (FunctionType) n.getJSType();
final String functionPrivateName = n.getFirstChild().getString();
if (functionPrivateName != null
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> && functionPrivateName.length() > 0 &&
outerScope.isDeclared(functionPrivateName, false) &&
// Ideally, we would want to check whether the type in the scope
// differs from the type being defined, but then the extern
// redeclarations of built-in types generates spurious warnings.
!(outerScope.getVar(
functionPrivateName).getType() instanceof FunctionType)) {
t.report(n, FUNCTION_MASKS_VARIABLE, functionPrivateName);
}
// TODO(user): Only traverse the function's body. The function's
// name and arguments are traversed by the scope creator, and ideally
// should not be traversed by the type checker.
break;
}
return true;
}
/**
* This is the meat of the type checking. It is basically one big switch,
* with each case representing one type of parse tree node. The individual
* cases are usually pretty straightforward.
*
* @param t The node traversal object that supplies context, such as the
* scope chain to use in name lookups as well as error reporting.
* @param n The node being visited.
* @param parent The parent of the node n.
*/
public void visit(NodeTraversal t, Node n, Node parent) {
JSType childType;
JSType leftType, rightType;
Node left, right;
// To be explicitly set to false if the node is not typeable.
boolean typeable = true;
switch (n.getType()) {
case Token.NAME:
typeable = visitName(t, n, parent);
break;
case Token.LP:
// If this is under a FUNCTION node, it is a parameter list and can be
// ignored here.
if (parent.getType() != Token.FUNCTION) {
ensureTyped(t, n, getJSType(n.getFirstChild()));
} else {
typeable = false;
}
break;
case Token.COMMA:
ensureTyped(t, n, getJSType(n.getLastChild()));
break;
case Token.TRUE:
case Token.FALSE:
ensureTyped(t, n, BOOLEAN_TYPE);
break;
case Token.THIS:
ensureTyped(t, n, t.getScope().getTypeOfThis());
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> break;
case Token.REF_SPECIAL:
ensureTyped(t, n);
break;
case Token.GET_REF:
ensureTyped(t, n, getJSType(n.getFirstChild()));
break;
case Token.NULL:
ensureTyped(t, n, NULL_TYPE);
break;
case Token.NUMBER:
if (n.getParent().getType() != Token.OBJECTLIT) {
ensureTyped(t, n, NUMBER_TYPE);
} else {
typeable = false;
}
break;
case Token.ARRAYLIT:
ensureTyped(t, n, ARRAY_TYPE);
break;
case Token.STRING:
if (n.getParent().getType() != Token.OBJECTLIT) {
ensureTyped(t, n, STRING_TYPE);
} else {
typeable = false;
}
break;
case Token.REGEXP:
ensureTyped(t, n, REGEXP_TYPE);
break;
case Token.GETPROP:
visitGetProp(t, n, parent);
typeable = !(parent.getType() == Token.ASSIGN &&
parent.getFirstChild() == n);
break;
case Token.GETELEM:
visitGetElem(t, n);
// The type of GETELEM is always unknown, so no point counting that.
// If that unknown leaks elsewhere (say by an assignment to another
// variable), then it will be counted.
typeable = false;
break;
case Token.VAR:
visitVar(t, n);
typeable = false;
break;
case Token.NEW:
visitNew(t, n);
typeable = true;
break;
case Token.CALL:
visitCall(t, n);
typeable = !NodeUtil.isExpressionNode(parent);
break;
case Token.RETURN:
visitReturn(t, n);
typeable = false;
break;
case Token.DEC:
case Token.INC:
left = n.getFirstChild();
validator.expectNumber(
t, left, getJSType(left), "increment/decrement");
ensureTyped(t, n, NUMBER_TYPE);
break;
case Token.NOT:
ensureTyped(t, n, BOOLEAN_TYPE);
break;
case Token.VOID:
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> ensureTyped(t, n, VOID_TYPE);
break;
case Token.TYPEOF:
ensureTyped(t, n, STRING_TYPE);
break;
case Token.BITNOT:
childType = getJSType(n.getFirstChild());
if (!childType.matchesInt32Context()) {
t.report(n, BIT_OPERATION, NodeUtil.opToStr(n.getType()),
childType.toString());
}
ensureTyped(t, n, NUMBER_TYPE);
break;
case Token.POS:
case Token.NEG:
left = n.getFirstChild();
validator.expectNumber(t, left, getJSType(left), "sign operator");
ensureTyped(t, n, NUMBER_TYPE);
break;
case Token.EQ:
case Token.NE: {
leftType = getJSType(n.getFirstChild());
rightType = getJSType(n.getLastChild());
JSType leftTypeRestricted = leftType.restrictByNotNullOrUndefined();
JSType rightTypeRestricted = rightType.restrictByNotNullOrUndefined();
TernaryValue result =
leftTypeRestricted.testForEquality(rightTypeRestricted);
if (result != TernaryValue.UNKNOWN) {
if (n.getType() == Token.NE) {
result = result.not();
}
t.report(n, DETERMINISTIC_TEST, leftType.toString(),
rightType.toString(), result.toString());
}
ensureTyped(t, n, BOOLEAN_TYPE);
break;
}
case Token.SHEQ:
case Token.SHNE: {
leftType = getJSType(n.getFirstChild());
rightType = getJSType(n.getLastChild());
JSType leftTypeRestricted = leftType.restrictByNotNullOrUndefined();
JSType rightTypeRestricted = rightType.restrictByNotNullOrUndefined();
if (!leftTypeRestricted.canTestForShallowEqualityWith(
rightTypeRestricted)) {
t.report(n, DETERMINISTIC_TEST_NO_RESULT, leftType.toString(),
rightType.toString());
}
ensureTyped(t, n, BOOLEAN_TYPE);
break;
}
case Token.LT:
case Token.LE:
case Token.GT:
case Token.GE:
left
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>Type = getJSType(n.getFirstChild());
rightType = getJSType(n.getLastChild());
if (rightType.isNumber()) {
validator.expectNumber(
t, n, leftType, "left side of numeric comparison");
} else if (leftType.isNumber()) {
validator.expectNumber(
t, n, rightType, "right side of numeric comparison");
} else if (leftType.matchesNumberContext() &&
rightType.matchesNumberContext()) {
// OK.
} else {
// Whether the comparison is numeric will be determined at runtime
// each time the expression is evaluated. Regardless, both operands
// should match a string context.
String message = "left side of comparison";
validator.expectString(t, n, leftType, message);
validator.expectNotVoid(
t, n, leftType, message, getNativeType(STRING_TYPE));
message = "right side of comparison";
validator.expectString(t, n, rightType, message);
validator.expectNotVoid(
t, n, rightType, message, getNativeType(STRING_TYPE));
}
ensureTyped(t, n, BOOLEAN_TYPE);
break;
case Token.IN:
left = n.getFirstChild();
right = n.getLastChild();
leftType = getJSType(left);
rightType = getJSType(right);
validator.expectObject(t, n, rightType, "'in' requires an object");
validator.expectString(t, left, leftType, "left side of 'in'");
ensureTyped(t, n, BOOLEAN_TYPE);
break;
case Token.INSTANCEOF:
left = n.getFirstChild();
right = n.getLastChild();
leftType = getJSType(left);
rightType = getJSType(right).restrictByNotNullOrUndefined();
validator.expectAnyObject(
t, left, leftType, "deterministic instanceof yields false");
validator.expectActualObject(
t, right, rightType, "instanceof requires an object");
ensureTyped(t, n, BOOLEAN_TYPE);
break;
case Token.ASSIGN:
visitAssign(t, n);
typeable = false;
break;
case Token.ASSIGN_LSH:
case Token.ASSIGN
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>_RSH:
case Token.ASSIGN_URSH:
case Token.ASSIGN_DIV:
case Token.ASSIGN_MOD:
case Token.ASSIGN_BITOR:
case Token.ASSIGN_BITXOR:
case Token.ASSIGN_BITAND:
case Token.ASSIGN_SUB:
case Token.ASSIGN_ADD:
case Token.ASSIGN_MUL:
case Token.LSH:
case Token.RSH:
case Token.URSH:
case Token.DIV:
case Token.MOD:
case Token.BITOR:
case Token.BITXOR:
case Token.BITAND:
case Token.SUB:
case Token.ADD:
case Token.MUL:
visitBinaryOperator(n.getType(), t, n);
break;
case Token.DELPROP:
if (!isReference(n.getFirstChild())) {
t.report(n, BAD_DELETE);
}
ensureTyped(t, n, BOOLEAN_TYPE);
break;
case Token.CASE:
JSType switchType = getJSType(parent.getFirstChild());
JSType caseType = getJSType(n.getFirstChild());
validator.expectSwitchMatchesCase(t, n, switchType, caseType);
typeable = false;
break;
case Token.WITH: {
Node child = n.getFirstChild();
childType = getJSType(child);
validator.expectObject(
t, child, childType, "with requires an object");
typeable = false;
break;
}
case Token.FUNCTION:
visitFunction(t, n);
break;
// These nodes have no interesting type behavior.
case Token.LABEL:
case Token.LABEL_NAME:
case Token.SWITCH:
case Token.BREAK:
case Token.CATCH:
case Token.TRY:
case Token.SCRIPT:
case Token.EXPR_RESULT:
case Token.BLOCK:
case Token.EMPTY:
case Token.DEFAULT:
case Token.CONTINUE:
case Token.DEBUGGER:
case Token.THROW:
typeable = false;
break;
// These nodes require data flow analysis.
case Token.DO:
case Token.FOR:
case Token.IF:
case Token.WHILE:
typeable = false;
break;
// These nodes are typed
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> (!namedType.isResolved()) {
return namedType.getReferenceName();
}
} else if (type.isUnionType()) {
for (JSType alt : ((UnionType) type).getAlternates()) {
if (alt.isUnknownType()) {
String unresolvedReference = getUnresolvedReference(alt);
if (unresolvedReference != null) {
return unresolvedReference;
}
}
}
}
return null;
}
/**
* Visits an assignment <code>lvalue = rvalue</code>. If the
* <code>lvalue</code> is a prototype modification, we change the schema
* of the object type it is referring to.
* @param t the traversal
* @param assign the assign node
* (<code>assign.getType() == Token.ASSIGN</code> is an implicit invariant)
*/
private void visitAssign(NodeTraversal t, Node assign) {
JSDocInfo info = assign.getJSDocInfo();
Node lvalue = assign.getFirstChild();
Node rvalue = assign.getLastChild();
if (lvalue.getType() == Token.GETPROP) {
Node object = lvalue.getFirstChild();
JSType objectJsType = getJSType(object);
String property = lvalue.getLastChild().getString();
// the first name in this getprop refers to an interface
// we perform checks in addition to the ones below
if (object.getType() == Token.GETPROP) {
JSType jsType = getJSType(object.getFirstChild());
if (jsType.isInterface() &&
object.getLastChild().getString().equals("prototype")) {
visitInterfaceGetprop(t, assign, object, property, lvalue, rvalue);
}
}
// /** @type ... */object.name = ...;
if (info != null && info.hasType()) {
visitAnnotatedAssignGetprop(t, assign,
info.getType().evaluate(t.getScope()), object,
property, rvalue);
return;
}
// /** @enum ... */object.name = ...;
if (info != null && info.hasEnumParameterType()) {
checkEnumInitializer(
t, rvalue, info.getEnumParameterType().evaluate(t.getScope()));
return;
}
//
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> object.prototype = ...;
if (property.equals("prototype")) {
if (objectJsType instanceof FunctionType) {
FunctionType functionType = (FunctionType) objectJsType;
if (functionType.isConstructor()) {
JSType rvalueType = rvalue.getJSType();
validator.expectObject(t, rvalue, rvalueType,
OVERRIDING_PROTOTYPE_WITH_NON_OBJECT);
}
} else {
// TODO(user): might want to flag that
}
return;
}
// object.prototype.property = ...;
if (object.getType() == Token.GETPROP) {
Node object2 = object.getFirstChild();
String property2 = NodeUtil.getStringValue(object.getLastChild());
if ("prototype".equals(property2)) {
JSType jsType = object2.getJSType();
if (jsType instanceof FunctionType) {
FunctionType functionType = (FunctionType) jsType;
if (functionType.isConstructor() || functionType.isInterface()) {
checkDeclaredPropertyInheritance(
t, assign, functionType, property, info, getJSType(rvalue));
}
} else {
// TODO(user): might want to flag that
}
return;
}
}
// object.property = ...;
ObjectType type = ObjectType.cast(
objectJsType.restrictByNotNullOrUndefined());
if (type != null) {
if (type.hasProperty(property) &&
!type.isPropertyTypeInferred(property) &&
!propertyIsImplicitCast(type, property)) {
validator.expectCanAssignToPropertyOf(
t, assign, getJSType(rvalue),
type.getPropertyType(property), object, property);
}
return;
}
} else if (lvalue.getType() == Token.NAME) {
// variable with inferred type case
JSType rvalueType = getJSType(assign.getLastChild());
Var var = t.getScope().getVar(lvalue.getString());
if (var != null) {
if (var.isTypeInferred()) {
return;
}
}
}
// fall through case
JSType leftType = getJSType(lvalue);
Node rightChild = assign.getLastChild();
JSType rightType = getJS
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> LP nodes) and
// variable declarations are ignored.
// TODO(user): remove this short-circuiting in favor of a
// pre order traversal of the FUNCTION, CATCH, LP and VAR nodes.
int parentNodeType = parent.getType();
if (parentNodeType == Token.FUNCTION ||
parentNodeType == Token.CATCH ||
parentNodeType == Token.LP ||
parentNodeType == Token.VAR) {
return false;
}
JSType type = n.getJSType();
if (type == null) {
type = getNativeType(UNKNOWN_TYPE);
Var var = t.getScope().getVar(n.getString());
if (var != null) {
JSType varType = var.getType();
if (varType != null) {
type = varType;
}
}
}
ensureTyped(t, n, type);
return true;
}
/**
* Visits a GETPROP node.
*
* @param t The node traversal object that supplies context, such as the
* scope chain to use in name lookups as well as error reporting.
* @param n The node being visited.
* @param parent The parent of <code>n</code>
*/
private void visitGetProp(NodeTraversal t, Node n, Node parent) {
// GETPROP nodes have an assigned type on their node by the scope creator
// if this is an enum declaration. The only namespaced enum declarations
// that we allow are of the form object.name = ...;
if (n.getJSType() != null && parent.getType() == Token.ASSIGN) {
return;
}
// obj.prop or obj.method()
// Lots of types can appear on the left, a call to a void function can
// never be on the left. getPropertyType will decide what is acceptable
// and what isn't.
Node property = n.getLastChild();
Node objNode = n.getFirstChild();
JSType childType = getJSType(objNode);
// TODO(user): remove in favor of flagging every property access on
// non-object.
if (!validator.expectNotVoid(t, n, childType,
"undefined has no properties", getNativeType(OBJECT_TYPE))) {
ensureTyped(t, n);
return;
}
check
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>PropertyAccess(childType, property.getString(), t, n);
ensureTyped(t, n);
}
/**
* Make sure that the access of this property is ok.
*/
private void checkPropertyAccess(JSType childType, String propName,
NodeTraversal t, Node n) {
ObjectType objectType = childType.dereference();
if (objectType != null) {
JSType propType = getJSType(n);
if ((!objectType.hasProperty(propName) ||
objectType.equals(typeRegistry.getNativeType(UNKNOWN_TYPE))) &&
propType.equals(typeRegistry.getNativeType(UNKNOWN_TYPE))) {
if (objectType instanceof EnumType) {
t.report(n, INEXISTENT_ENUM_ELEMENT, propName);
} else if (!objectType.isEmptyType() &&
reportMissingProperties && !isPropertyTest(n)) {
if (!typeRegistry.canPropertyBeDefined(objectType, propName)) {
t.report(n, INEXISTENT_PROPERTY, propName,
validator.getReadableJSTypeName(n.getFirstChild(), true));
}
}
}
} else {
// TODO(nicksantos): might want to flag the access on a non object when
// it's impossible to get a property from this type.
}
}
/**
* Determines whether this node is testing for the existence of a property.
* If true, we will not emit warnings about a missing property.
*
* @param getProp The GETPROP being tested.
*/
private boolean isPropertyTest(Node getProp) {
Node parent = getProp.getParent();
switch (parent.getType()) {
case Token.CALL:
return parent.getFirstChild() != getProp &&
compiler.getCodingConvention().isPropertyTestFunction(parent);
case Token.IF:
case Token.WHILE:
case Token.DO:
case Token.FOR:
return NodeUtil.getConditionExpression(parent) == getProp;
case Token.INSTANCEOF:
case Token.TYPEOF:
return true;
case Token.AND:
case Token.HOOK:
return parent.getFirstChild() == getProp;
}
return false;
}
/**
* Visits a GETELEM node.
*
*
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> @param t The node traversal object that supplies context, such as the
* scope chain to use in name lookups as well as error reporting.
* @param n The node being visited.
*/
private void visitGetElem(NodeTraversal t, Node n) {
Node left = n.getFirstChild();
Node right = n.getLastChild();
validator.expectIndexMatch(t, n, getJSType(left), getJSType(right));
ensureTyped(t, n);
}
/**
* Visits a VAR node.
*
* @param t The node traversal object that supplies context, such as the
* scope chain to use in name lookups as well as error reporting.
* @param n The node being visited.
*/
private void visitVar(NodeTraversal t, Node n) {
// TODO(nicksantos): Fix this so that the doc info always shows up
// on the NAME node. We probably want to wait for the parser
// merge to fix this.
JSDocInfo varInfo = n.hasOneChild() ? n.getJSDocInfo() : null;
for (Node name : n.children()) {
Node value = name.getFirstChild();
// A null var would indicate a bug in the scope creation logic.
Var var = t.getScope().getVar(name.getString());
if (value != null) {
JSType valueType = getJSType(value);
JSType nameType = var.getType();
nameType = (nameType == null) ? getNativeType(UNKNOWN_TYPE) : nameType;
JSDocInfo info = name.getJSDocInfo();
if (info == null) {
info = varInfo;
}
if (info != null && info.hasEnumParameterType()) {
// var.getType() can never be null, this would indicate a bug in the
// scope creation logic.
checkEnumInitializer(
t, value, info.getEnumParameterType().evaluate(t.getScope()));
} else if (var.isTypeInferred()) {
ensureTyped(t, name, valueType);
} else {
validator.expectCanAssignTo(
t, value, valueType, nameType, "initializing variable");
}
}
}
}
/**
* Visits a
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> NEW node.
*/
private void visitNew(NodeTraversal t, Node n) {
Node constructor = n.getFirstChild();
FunctionType type = getFunctionType(constructor);
if (type != null && type.isConstructor()) {
visitParameterList(t, n, type);
ensureTyped(t, n, type.getInstanceType());
} else {
// TODO(user): add support for namespaced objects.
if (constructor.getType() != Token.GETPROP) {
// TODO(user): make the constructor node have lineno/charno
// and use constructor for a more precise error indication.
// It seems that GETPROP nodes are missing this information.
Node line;
if (constructor.getLineno() < 0 || constructor.getCharno() < 0) {
line = n;
} else {
line = constructor;
}
t.report(line, NOT_A_CONSTRUCTOR);
}
ensureTyped(t, n);
}
}
/**
* Visits a {@link Token#FUNCTION} node.
*
* @param t The node traversal object that supplies context, such as the
* scope chain to use in name lookups as well as error reporting.
* @param n The node being visited.
*/
private void visitFunction(NodeTraversal t, Node n) {
JSDocInfo info = n.getJSDocInfo();
FunctionType functionType = (FunctionType) n.getJSType();
String functionPrivateName = n.getFirstChild().getString();
if (functionType.isInterface() || functionType.isConstructor()) {
FunctionType baseConstructor = functionType.
getPrototype().getImplicitPrototype().getConstructor();
if (baseConstructor != null &&
baseConstructor != getNativeType(OBJECT_FUNCTION_TYPE) &&
(baseConstructor.isConstructor() && functionType.isInterface() ||
baseConstructor.isInterface() && functionType.isConstructor())) {
compiler.report(
t.makeError(n, CONFLICTING_EXTENDED_TYPE, functionPrivateName));
}
for (JSType baseInterface : functionType.getImplementedInterfaces()) {
boolean badImplementedType = false;
ObjectType baseInterfaceObj = ObjectType.cast(baseInterface);
if (baseInterfaceObj != null) {
FunctionType interfaceConstructor
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> =
baseInterfaceObj.getConstructor();
if (interfaceConstructor != null &&
!interfaceConstructor.isInterface()) {
badImplementedType = true;
}
} else {
badImplementedType = true;
}
if (badImplementedType) {
t.report(n, BAD_IMPLEMENTED_TYPE, functionPrivateName);
}
}
if (functionType.isConstructor()) {
validator.expectAllInterfacePropertiesImplemented(functionType);
}
}
}
/**
* Visits a CALL node.
*
* @param t The node traversal object that supplies context, such as the
* scope chain to use in name lookups as well as error reporting.
* @param n The node being visited.
*/
private void visitCall(NodeTraversal t, Node n) {
Node child = n.getFirstChild();
JSType childType = getJSType(child).restrictByNotNullOrUndefined();
if (!childType.canBeCalled()) {
t.report(n, NOT_CALLABLE, childType.toString());
ensureTyped(t, n);
return;
}
// A couple of types can be called as if they were functions.
// If it is a function type, then validate parameters.
if (childType instanceof FunctionType) {
FunctionType functionType = (FunctionType) childType;
// Non-native constructors should never be called directly.
if (functionType.isConstructor() &&
!functionType.isNativeObjectType()) {
t.report(n, CONSTRUCTOR_NOT_CALLABLE, childType.toString());
}
visitParameterList(t, n, functionType);
ensureTyped(t, n, functionType.getReturnType());
} else {
ensureTyped(t, n);
}
// TODO: Add something to check for calls of RegExp objects, which is not
// supported by IE. Either say something about the return type or warn
// about the non-portability of the call or both.
}
/**
* Visits the parameters of a CALL or a NEW node.
*/
private void visitParameterList(NodeTraversal t, Node call,
FunctionType functionType) {
Iterator<Node> arguments = call.children().iterator();
arguments.next(); // skip the function name
Iterator<Node> parameters = functionType.
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>getParameters().iterator();
int ordinal = 0;
while (arguments.hasNext() && parameters.hasNext()) {
Node parameter = parameters.next();
Node argument = arguments.next();
ordinal++;
validator.expectArgumentMatchesParameter(t, argument,
getJSType(argument), getJSType(parameter), call, ordinal);
}
int numArgs = call.getChildCount() - 1;
int minArgs = functionType.getMinArguments();
int maxArgs = functionType.getMaxArguments();
if (minArgs > numArgs || maxArgs < numArgs) {
t.getCompiler().report(
t.makeError(call, WRONG_ARGUMENT_COUNT,
validator.getReadableJSTypeName(call.getFirstChild(), false),
String.valueOf(numArgs), String.valueOf(minArgs),
maxArgs != Integer.MAX_VALUE ?
" and no more than " + maxArgs + " argument(s)" : ""));
}
}
/**
* Visits a RETURN node.
*
* @param t The node traversal object that supplies context, such as the
* scope chain to use in name lookups as well as error reporting.
* @param n The node being visited.
*/
private void visitReturn(NodeTraversal t, Node n) {
Node function = t.getEnclosingFunction();
// This is a misplaced return, but the real JS will fail to compile,
// so let it go.
if (function == null) {
return;
}
JSType jsType = getJSType(function);
if (jsType instanceof FunctionType) {
FunctionType functionType = (FunctionType) jsType;
JSType returnType = functionType.getReturnType();
// if no return type is specified, undefined must be returned
// (it's a void function)
if (returnType == null) {
returnType = getNativeType(VOID_TYPE);
}
// fetching the returned value's type
Node valueNode = n.getFirstChild();
JSType actualReturnType;
if (valueNode == null) {
actualReturnType = getNativeType(VOID_TYPE);
valueNode = n;
} else {
actualReturnType = getJSType(valueNode);
}
// verifying
validator.expectCanAssignTo(t, valueNode, actualReturnType, returnType,
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>
"inconsistent return type");
}
}
/**
* This function unifies the type checking involved in the core binary
* operators and the corresponding assignment operators. The representation
* used internally is such that common code can handle both kinds of
* operators easily.
*
* @param op The operator.
* @param t The traversal object, needed to report errors.
* @param n The node being checked.
*/
private void visitBinaryOperator(int op, NodeTraversal t, Node n) {
Node left = n.getFirstChild();
JSType leftType = getJSType(left);
Node right = n.getLastChild();
JSType rightType = getJSType(right);
switch (op) {
case Token.ASSIGN_LSH:
case Token.ASSIGN_RSH:
case Token.LSH:
case Token.RSH:
case Token.ASSIGN_URSH:
case Token.URSH:
if (!leftType.matchesInt32Context()) {
t.report(left, BIT_OPERATION,
NodeUtil.opToStr(n.getType()), leftType.toString());
}
if (!rightType.matchesUint32Context()) {
t.report(right, BIT_OPERATION,
NodeUtil.opToStr(n.getType()), rightType.toString());
}
break;
case Token.ASSIGN_DIV:
case Token.ASSIGN_MOD:
case Token.ASSIGN_MUL:
case Token.ASSIGN_SUB:
case Token.DIV:
case Token.MOD:
case Token.MUL:
case Token.SUB:
validator.expectNumber(t, left, leftType, "left operand");
validator.expectNumber(t, right, rightType, "right operand");
break;
case Token.ASSIGN_BITAND:
case Token.ASSIGN_BITXOR:
case Token.ASSIGN_BITOR:
case Token.BITAND:
case Token.BITXOR:
case Token.BITOR:
validator.expectBitwiseable(t, left, leftType,
"bad left operand to bitwise operator");
validator.expectBitwiseable(t, right, rightType,
"bad right operand to bitwise operator");
break;
case Token.ASSIGN_ADD:
case Token.ADD:
break;
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> default:
t.report(n, UNEXPECTED_TOKEN, Node.tokenToName(op));
}
ensureTyped(t, n);
}
/**
* <p>Checks the initializer of an enum. An enum can be initialized with an
* object literal whose values must be subtypes of the declared enum element
* type, or by copying another enum.</p>
*
* <p>In the case of an enum copy, we verify that the enum element type of the
* enum used for initialization is a subtype of the enum element type of
* the enum the value is being copied in.</p>
*
* <p>Examples:</p>
* <pre>var myEnum = {FOO: ..., BAR: ...};
* var myEnum = myOtherEnum;</pre>
*
* @param value the value used for initialization of the enum
* @param primitiveType The type of each element of the enum.
*/
private void checkEnumInitializer(
NodeTraversal t, Node value, JSType primitiveType) {
if (value.getType() == Token.OBJECTLIT) {
// re-using value as the value of the object literal and advancing twice
value = value.getFirstChild();
value = (value == null) ? null : value.getNext();
while (value != null) {
// the value's type must be assignable to the enum's primitive type
validator.expectCanAssignTo(t, value, getJSType(value), primitiveType,
"element type must match enum's type");
// advancing twice
value = value.getNext();
value = (value == null) ? null : value.getNext();
}
} else if (value.getJSType() instanceof EnumType) {
// TODO(user): Remove the instanceof check in favor
// of a type.isEnumType() predicate. Currently, not all enum types are
// implemented by the EnumClass, e.g. the unknown type and the any
// type. The types need to be defined by interfaces such that an
// implementation can implement multiple types interface.
EnumType valueEnumType = (EnumType) value.getJSType();
JSType valueEnumPrimitiveType =
valueEnumType.getElementsType().getPrimitiveType();
validator.expectCanAssignTo(t, value, valueEnumPrimitiveType,
primitive
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>
Preconditions.checkState(expectedScopes.size() == actualScopes.size());
for (int i = 0; i < expectedScopes.size(); i++) {
Scope expectedScope = expectedScopes.get(i);
Scope actualScope = actualScopes.get(i);
if (!checkNodesMatch(expectedScope.getRootNode(),
actualScope.getRootNode())) {
compiler.report(
JSError.make(
SCOPE_MISMATCH,
expectedScope.getRootNode().toStringTree(),
actualScope.getRootNode().toStringTree()));
continue;
}
if (expectedScope.getVarCount() != actualScope.getVarCount()) {
compiler.report(
JSError.make(
VARIABLE_COUNT_MISMATCH,
Integer.toString(expectedScope.getVarCount()),
Integer.toString(actualScope.getVarCount())));
} else {
Iterator<Var> it = expectedScope.getVars();
while (it.hasNext()) {
Var var = it.next();
Scope.Var actualVar = actualScope.getVar(var.getName());
if (actualVar == null ||
expectedScope.getVar(var.getName()) != var) {
compiler.report(
JSError.make(MISSING_VARIABLE, var.getName()));
} else if (
!checkNodesMatch(
var.getNameNode(),
actualVar.getNameNode()) ||
!isNodeAttached(actualVar.getNameNode())) {
compiler.report(
JSError.make(MOVED_VARIABLE, var.getName()));
}
}
}
}
}
/**
* Check that the two nodes have the same relative position in the tree.
*/
private boolean checkNodesMatch(Node nodeA, Node nodeB) {
Node currentA = nodeA;
Node currentB = nodeB;
while (currentA != null && currentB != null) {
if (currentA.getType() != currentB.getType() ||
!currentA.isEquivalentTo(currentB)) {
return false;
}
currentA = currentA.getParent();
currentB = currentB.getParent();
}
return currentA == null && currentB == null;
}
private boolean isNodeAttached(Node node) {
// Make sure the cached var is still attached.
for (Node current = node;
current != null; current
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> // to that node.
if (fileOverviewInfo != null) {
if ((irNode.getJSDocInfo() != null) &&
(irNode.getJSDocInfo().getLicense() != null)) {
fileOverviewInfo.setLicense(irNode.getJSDocInfo().getLicense());
}
irNode.setJSDocInfo(fileOverviewInfo);
}
}
return irNode;
}
private Node transformBlock(AstNode node) {
Node irNode = transform(node);
if (irNode.getType() != Token.BLOCK) {
if (irNode.getType() == Token.EMPTY) {
irNode.setType(Token.BLOCK);
irNode.setWasEmptyNode(true);
} else {
Node newBlock = new Node(Token.BLOCK, irNode,
irNode.getLineno(), irNode.getCharno());
irNode = newBlock;
}
}
return irNode;
}
private Node transform(AstNode node) {
String jsDoc = node.getJsDoc();
NodeWithJsDoc nodeWithJsDoc = null;
if (jsDoc != null) {
nodeWithJsDoc = new NodeWithJsDoc();
nodesWithJsDoc.put(jsDoc, nodeWithJsDoc);
}
Node irNode = justTransform(node);
if (nodeWithJsDoc != null) {
nodeWithJsDoc.node = irNode;
}
// If we have a named function, set the position to that of the name.
if (irNode.getType() == Token.FUNCTION &&
irNode.getFirstChild().getLineno() != -1) {
irNode.setLineno(irNode.getFirstChild().getLineno());
irNode.setCharno(irNode.getFirstChild().getCharno());
} else {
if (irNode.getLineno() == -1) {
// If we didn't already set the line, then set it now. This avoids
// cases like ParenthesizedExpression where we just return a previous
// node, but don't want the new node to get its parent's line number.
int lineno = node.getLineno();
irNode.setLineno(lineno);
int charno = position2charno(node.get
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> if (node.getType() == Token.NAME) {
registry.identifyEnumName(node.getString());
} else if (node.getType() == Token.VAR &&
node.getChildCount() == 1) {
registry.identifyEnumName(
node.getFirstChild().getString());
} else if (node.getType() == Token.ASSIGN) {
registry.identifyEnumName(
node.getFirstChild().getQualifiedName());
}
}
}
private int position2charno(int position) {
int lineIndex = sourceString.lastIndexOf('\n', position);
if (lineIndex == -1) {
return position;
} else {
// Subtract one for initial position being 0.
return position - lineIndex - 1;
}
}
private Node justTransform(AstNode node) {
return transformDispatcher.process(node);
}
private class TransformDispatcher extends TypeSafeDispatcher<Node> {
private Node processGeneric(
com.google.javascript.jscomp.mozilla.rhino.Node n) {
Node node = new Node(transformTokenType(n.getType()));
for (com.google.javascript.jscomp.mozilla.rhino.Node child : n) {
node.addChildToBack(transform((AstNode)child));
}
return node;
}
/**
* Transforms the given node and then sets its type to Token.STRING if it
* was Token.NAME. If its type was already Token.STRING, then quotes it.
* Used for properties, as the old AST uses String tokens, while the new one
* uses Name tokens for unquoted strings. For example, in
* var o = {'a' : 1, b: 2};
* the string 'a' is quoted, while the name b is turned into a string, but
* unquoted.
*/
private Node transformAsString(AstNode n) {
Node ret = transform(n);
if (ret.getType() == Token.STRING) {
ret.putBooleanProp(Node.QUOTED_PROP, true);
} else if (ret.getType() == Token.NAME) {
ret.setType(Token.STRING);
}
return ret;
}
@Override
Node processArrayLiteral(ArrayLiteral literalNode) {
if (literalNode.isDestruct
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>uring()) {
reportDestructuringAssign(literalNode);
}
Node node = new Node(Token.ARRAYLIT);
int skipCount = 0;
for (AstNode child : literalNode.getElements()) {
Node c = transform(child);
if (c.getType() == Token.EMPTY) {
skipCount++;
}
node.addChildToBack(c);
}
if (skipCount > 0) {
int[] skipIndexes = new int[skipCount];
int i = 0;
int j = 0;
for (Node child : node.children()) {
if (child.getType() == Token.EMPTY) {
node.removeChild(child);
skipIndexes[j] = i;
j++;
}
i++;
}
node.putProp(Node.SKIP_INDEXES_PROP, skipIndexes);
}
return node;
}
@Override
Node processAssignment(Assignment assignmentNode) {
return processInfixExpression(assignmentNode);
}
@Override
Node processAstRoot(AstRoot rootNode) {
Node node = new ScriptOrFnNode(Token.SCRIPT);
for (com.google.javascript.jscomp.mozilla.rhino.Node child : rootNode) {
node.addChildToBack(transform((AstNode)child));
}
parseDirectives(node);
return node;
}
/**
* Parse the directives, encode them in the AST, and remove their nodes.
*
* For information on ES5 directives, see section 14.1 of
* Ecma-262, Edition 5.
*
* It would be nice if Rhino would eventually take care of this for
* us, but right now their directive-processing is a one-off.
*/
private void parseDirectives(Node node) {
// Remove all the directives, and encode them in the AST.
Set<String> directives = null;
while (isDirective(node.getFirstChild())) {
String directive = node.removeFirstChild().getFirstChild().getString();
if (directives == null) {
directives = Sets.newHashSet(directive);
} else {
directives.add(directive);
}
}
if (directives != null) {
node.setDirectives(directives);
}
}
private
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> boolean isDirective(Node n) {
if (n == null) return false;
int nType = n.getType();
return (nType == Token.EXPR_RESULT || nType == Token.EXPR_VOID) &&
n.getFirstChild().getType() == Token.STRING &&
ALLOWED_DIRECTIVES.contains(n.getFirstChild().getString());
}
@Override
Node processBlock(Block blockNode) {
return processGeneric(blockNode);
}
@Override
Node processBreakStatement(BreakStatement statementNode) {
Node node = new Node(Token.BREAK);
if (statementNode.getBreakLabel() != null) {
Node labelName = transform(statementNode.getBreakLabel());
// Change the NAME to LABEL_NAME
labelName.setType(Token.LABEL_NAME);
node.addChildToBack(labelName);
}
return node;
}
@Override
Node processCatchClause(CatchClause clauseNode) {
AstNode catchVar = clauseNode.getVarName();
Node node = new Node(Token.CATCH, transform(catchVar));
if (clauseNode.getCatchCondition() != null) {
node.addChildToBack(transform(clauseNode.getCatchCondition()));
} else {
Node catchCondition = new Node(Token.EMPTY);
// Old Rhino used the position of the catchVar as the position
// for the (nonexistent) error being caught.
catchCondition.setLineno(catchVar.getLineno());
int clauseAbsolutePosition =
position2charno(catchVar.getAbsolutePosition());
catchCondition.setCharno(clauseAbsolutePosition);
node.addChildToBack(catchCondition);
}
node.addChildToBack(transformBlock(clauseNode.getBody()));
return node;
}
@Override
Node processConditionalExpression(ConditionalExpression exprNode) {
return new Node(
Token.HOOK,
transform(exprNode.getTestExpression()),
transform(exprNode.getTrueExpression()),
transform(exprNode.getFalseExpression()));
}
@Override
Node processContinueStatement(ContinueStatement statementNode) {
Node node = new Node(Token.CONTINUE);
if (statementNode.getLabel() != null) {
Node labelName = transform(statementNode.getLabel());
// Change the NAME to LABEL_NAME
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>
}
Node parseTestCode(String js) {
initCompilerOptionsIfTesting();
CompilerInput input = new CompilerInput(
JSSourceFile.fromCode(" [testcode] ", js));
if (inputsByName == null) {
inputsByName = Maps.newHashMap();
}
inputsByName.put(input.getName(), input);
return input.getAstRoot(this);
}
@Override
ErrorReporter getDefaultErrorReporter() {
return defaultErrorReporter;
}
//------------------------------------------------------------------------
// Convert back to source code
//------------------------------------------------------------------------
/**
* Converts the main parse tree back to js code.
*/
public String toSource() {
return runInCompilerThread(new Callable<String>() {
public String call() throws Exception {
Tracer tracer = newTracer("toSource");
try {
CodeBuilder cb = new CodeBuilder();
if (jsRoot != null) {
int i = 0;
for (Node scriptNode = jsRoot.getFirstChild();
scriptNode != null;
scriptNode = scriptNode.getNext()) {
toSource(cb, i++, scriptNode);
}
}
return cb.toString();
} finally {
stopTracer(tracer, "toSource");
}
}
});
}
/**
* Converts the parse tree for each input back to js code.
*/
public String[] toSourceArray() {
return runInCompilerThread(new Callable<String[]>() {
public String[] call() throws Exception {
Tracer tracer = newTracer("toSourceArray");
try {
int numInputs = inputs.size();
String[] sources = new String[numInputs];
CodeBuilder cb = new CodeBuilder();
for (int i = 0; i < numInputs; i++) {
Node scriptNode = inputs.get(i).getAstRoot(Compiler.this);
cb.reset();
toSource(cb, i, scriptNode);
sources[i] = cb.toString();
}
return sources;
} finally {
stopTracer(tracer, "toSourceArray");
}
}
});
}
/**
* Converts the parse tree for a module back to js code.
*/
public String toSource(final JSModule module) {
return runInCompilerThread(new Callable<String>() {
public String call
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> null;
if (parent == null) {
scope = new Scope(n, compiler);
} else {
scope = new Scope(parent, n);
}
scanRoot(n, parent);
sourceName = null;
Scope returnedScope = scope;
scope = null;
return returnedScope;
}
private void scanRoot(Node n, Scope parent) {
if (n.getType() == Token.FUNCTION) {
sourceName = (String) n.getProp(Node.SOURCENAME_PROP);
final Node fnNameNode = n.getFirstChild();
final Node args = fnNameNode.getNext();
final Node body = args.getNext();
// Bleed the function name into the scope, if it hasn't
// been declared in the outer scope.
String fnName = fnNameNode.getString();
if (!fnName.isEmpty() && NodeUtil.isFunctionAnonymous(n)) {
declareVar(fnName, fnNameNode, n, null, null, n);
}
// Args: Declare function variables
Preconditions.checkState(args.getType() == Token.LP);
for (Node a = args.getFirstChild(); a != null;
a = a.getNext()) {
Preconditions.checkState(a.getType() == Token.NAME);
declareVar(a.getString(), a, args, n, null, n);
}
// Body
scanVars(body, n);
} else {
// It's the global block
Preconditions.checkState(scope.getParent() == null);
scanVars(n, null);
}
}
/**
* Scans and gather variables declarations under a Node
*/
private void scanVars(Node n, Node parent) {
switch (n.getType()) {
case Token.VAR:
// Declare all variables. e.g. var x = 1, y, z;
for (Node child = n.getFirstChild();
child != null;) {
Node next = child.getNext();
Preconditions.checkState(child.getType() == Token.NAME);
String name = child.getString();
declareVar(name, child, n, parent, null, n);
child = next;
}
return;
case Token.FUNCTION:
if (NodeUtil.isFunctionAnonymous(n)) {
return;
}
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>
String fnName = n.getFirstChild().getString();
if (fnName.isEmpty()) {
// This is invalid, but allow it so the checks can catch it.
return;
}
declareVar(fnName, n.getFirstChild(), n, parent, null, n);
return; // should not examine function's children
case Token.CATCH:
Preconditions.checkState(n.getChildCount() == 3);
Preconditions.checkState(n.getFirstChild().getType() == Token.NAME);
// the first child is the catch var and the third child
// is the code block
final Node var = n.getFirstChild();
final Node block = var.getNext().getNext();
declareVar(var.getString(), var, n, parent, null, n);
scanVars(block, n);
return; // only one child to scan
case Token.SCRIPT:
sourceName = (String) n.getProp(Node.SOURCENAME_PROP);
break;
}
// Variables can only occur in statement-level nodes, so
// we only need to traverse children in a couple special cases.
if (NodeUtil.isControlStructure(n) || NodeUtil.isStatementBlock(n)) {
for (Node child = n.getFirstChild();
child != null;) {
Node next = child.getNext();
scanVars(child, n);
child = next;
}
}
}
/**
* Interface for injectable duplicate handling.
*/
interface RedeclarationHandler {
void onRedeclaration(
Scope s, String name,
Node n, Node parent, Node gramps, Node nodeWithLineNumber);
}
/**
* The default handler for duplicate declarations.
*/
private class DefaultRedeclarationHandler implements RedeclarationHandler {
public void onRedeclaration(
Scope s, String name,
Node n, Node parent, Node gramps, Node nodeWithLineNumber) {
// Don't allow multiple variables to be declared at the top level scope
if (scope.isGlobal()) {
Scope.Var origVar = scope.getVar(name);
Node origParent = origVar.getParentNode();
if (origParent.getType() == Token.CATCH &&
parent.getType() == Token.CATCH) {
// Okay, both are 'catch(x)' variables.
return
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>/*
* Copyright 2009 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.Token;
import com.google.javascript.rhino.jstype.FunctionType;
import com.google.javascript.rhino.jstype.JSType;
import com.google.javascript.rhino.jstype.ObjectType;
import java.nio.charset.Charset;
/**
* A code generator that outputs type annotations for functions and
* constructors.
*
*/
class TypedCodeGenerator extends CodeGenerator {
TypedCodeGenerator(CodeConsumer consumer, Charset outputCharset) {
super(consumer, outputCharset, true);
}
@Override
void add(Node n, Context context) {
Node parent = n.getParent();
if (parent.getType() == Token.BLOCK || parent.getType() == Token.SCRIPT) {
if (n.getType() == Token.FUNCTION) {
add(getFunctionAnnotation(n));
} else if (n.getType() == Token.EXPR_RESULT
&& n.getFirstChild().getType() == Token.ASSIGN) {
Node rhs = n.getFirstChild().getFirstChild();
add(getTypeAnnotation(rhs));
} else if (n.getType() == Token.VAR
&& n.getFirstChild().getFirstChild() != null
&& n.getFirstChild().getFirstChild().getType() == Token.FUNCTION) {
add(getFunctionAnnotation(n.getFirstChild().getFirstChild()));
}
}
super.add(n, context);
}
private String getTypeAnnotation(Node node) {
JSType type = node.getJSType();
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>
if (type instanceof FunctionType) {
return getFunctionAnnotation(node);
} else if (type != null && !type.isUnknownType()
&& !type.isEmptyType() && !type.isVoidType()) {
return "/** @type {" + node.getJSType() + "} */\n";
} else {
return "";
}
}
/**
* @param node A node for a function for which to generate a type annotation
*/
private String getFunctionAnnotation(Node node) {
StringBuilder sb = new StringBuilder("/**\n");
if (node.getJSType().isUnknownType()) {
return "";
}
FunctionType funType = (FunctionType) node.getJSType();
// We need to use the child nodes of the function as the nodes for the
// parameters of the function type do not have the real parameter names.
// FUNCTION
// NAME
// LP
// NAME param1
// NAME param2
Node fnNode = funType.getSource();
if (fnNode != null) {
Node paramNode = NodeUtil.getFnParameters(fnNode).getFirstChild();
// Param types
for (Node n : funType.getParameters()) {
// Bail out if the paramNode is not there.
if (paramNode == null) {
break;
}
sb.append(" * @param {" + n.getJSType() + "} ");
sb.append(paramNode.getString());
sb.append("\n");
paramNode = paramNode.getNext();
}
}
// Return type
JSType retType = funType.getReturnType();
if (retType != null && !retType.isUnknownType() && !retType.isEmptyType()) {
sb.append(" * @return {" + retType + "}\n");
}
// Constructor/interface
if (funType.isConstructor() || funType.isInterface()) {
ObjectType superInstance =
funType.getSuperClassConstructor().getInstanceType();
if (!superInstance.toString().equals("Object")) {
sb.append(" * @extends {" + superInstance + "}\n");
}
for (ObjectType interfaze : funType.getImplementedInterfaces()) {
sb.append(" * @implements {" + interfaze + "}\n");
}
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>
private JSType currentClass = null;
CheckAccessControls(AbstractCompiler compiler) {
this.compiler = compiler;
this.validator = compiler.getTypeValidator();
}
public void process(Node externs, Node root) {
NodeTraversal.traverse(compiler, root, this);
}
public void enterScope(NodeTraversal t) {
if (!t.inGlobalScope()) {
Node n = t.getScopeRoot();
Node parent = n.getParent();
if (isDeprecatedFunction(n, parent)) {
deprecatedDepth++;
}
if (methodDepth == 0) {
currentClass = getClassOfMethod(n, parent);
}
methodDepth++;
}
}
public void exitScope(NodeTraversal t) {
if (!t.inGlobalScope()) {
Node n = t.getScopeRoot();
Node parent = n.getParent();
if (isDeprecatedFunction(n, parent)) {
deprecatedDepth--;
}
methodDepth--;
if (methodDepth == 0) {
currentClass = null;
}
}
}
/**
* Gets the type of the class that "owns" a method, or null if
* we know that its un-owned.
*/
private JSType getClassOfMethod(Node n, Node parent) {
if (parent.getType() == Token.ASSIGN) {
Node lValue = parent.getFirstChild();
if (lValue.isQualifiedName()) {
if (lValue.getType() == Token.GETPROP) {
// We have an assignment of the form "a.b = ...".
JSType lValueType = lValue.getJSType();
if (lValueType != null && lValueType.isConstructor()) {
// If a.b is a constructor, then everything in this function
// belongs to the "a.b" type.
return ((FunctionType) lValueType).getInstanceType();
} else {
// If a.b is not a constructor, then treat this as a method
// of whatever type is on "a".
return normalizeClassType(lValue.getFirstChild().getJSType());
}
} else {
// We have an assignment of the form "a = ...", so pull the
// type off the "a".
return normalizeClassType(lValue.getJSType());
}
}
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>precation(NodeTraversal t, Node n, Node parent) {
// Don't bother checking definitions or constructors.
if (parent.getType() == Token.FUNCTION || parent.getType() == Token.VAR ||
parent.getType() == Token.NEW) {
return;
}
Scope.Var var = t.getScope().getVar(n.getString());
JSDocInfo docInfo = var == null ? null : var.getJSDocInfo();
if (docInfo != null && docInfo.isDeprecated() &&
shouldEmitDeprecationWarning(t, n, parent)) {
if (docInfo.getDeprecationReason() != null) {
compiler.report(
t.makeError(n, DEPRECATED_NAME_REASON, n.getString(),
docInfo.getDeprecationReason()));
} else {
compiler.report(
t.makeError(n, DEPRECATED_NAME, n.getString()));
}
}
}
/**
* Checks the given GETPROP node to ensure that access restrictions are
* obeyed.
*/
private void checkPropertyDeprecation(NodeTraversal t, Node n, Node parent) {
// Don't bother checking constructors.
if (parent.getType() == Token.NEW) {
return;
}
ObjectType objectType =
ObjectType.cast(dereference(n.getFirstChild().getJSType()));
String propertyName = n.getLastChild().getString();
if (objectType != null) {
String deprecationInfo
= getPropertyDeprecationInfo(objectType, propertyName);
if (deprecationInfo != null &&
shouldEmitDeprecationWarning(t, n, parent)) {
if (!deprecationInfo.isEmpty()) {
compiler.report(
t.makeError(n, DEPRECATED_PROP_REASON, propertyName,
validator.getReadableJSTypeName(n.getFirstChild(), true),
deprecationInfo));
} else {
compiler.report(
t.makeError(n, DEPRECATED_PROP, propertyName,
validator.getReadableJSTypeName(n.getFirstChild(), true)));
}
}
}
}
/**
* Determines whether the given name is visible in the current context.
* @param t The current traversal.
* @param name The name node.
*/
private void checkNameVisibility(NodeTraversal t, Node name, Node parent) {
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> Var var = t.getScope().getVar(name.getString());
if (var != null) {
JSDocInfo docInfo = var.getJSDocInfo();
if (docInfo != null) {
// If a name is private, make sure that we're in the same file.
Visibility visibility = docInfo.getVisibility();
if (visibility == Visibility.PRIVATE &&
!t.getInput().getName().equals(docInfo.getSourceName())) {
if (docInfo.isConstructor() &&
isValidPrivateConstructorAccess(parent)) {
return;
}
compiler.report(
t.makeError(name, BAD_PRIVATE_GLOBAL_ACCESS,
name.getString(), docInfo.getSourceName()));
}
}
}
}
/**
* Determines whether the given property is visible in the current context.
* @param t The current traversal.
* @param getprop The getprop node.
*/
private void checkPropertyVisibility(NodeTraversal t,
Node getprop, Node parent) {
ObjectType objectType =
ObjectType.cast(dereference(getprop.getFirstChild().getJSType()));
String propertyName = getprop.getLastChild().getString();
if (objectType != null) {
// Is this a normal property access, or are we trying to override
// an existing property?
boolean isOverride = t.inGlobalScope() &&
parent.getType() == Token.ASSIGN &&
parent.getFirstChild() == getprop;
// Find the lowest property defined on a class with visibility
// information.
if (isOverride) {
objectType = objectType.getImplicitPrototype();
}
JSDocInfo docInfo = null;
for (; objectType != null;
objectType = objectType.getImplicitPrototype()) {
docInfo = objectType.getOwnPropertyJSDocInfo(propertyName);
if (docInfo != null &&
docInfo.getVisibility() != Visibility.INHERITED) {
break;
}
}
if (objectType == null) {
// We couldn't find a visibility modifier; assume it's public.
return;
}
boolean sameInput =
t.getInput().getName().equals(docInfo.getSourceName());
Visibility visibility = docInfo.getVisibility();
JSType ownerType = normalizeClassType(objectType);
if (isOverride)
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> {
// Check an ASSIGN statement that's trying to override a property
// on a superclass.
JSDocInfo overridingInfo = parent.getJSDocInfo();
Visibility overridingVisibility = overridingInfo == null ?
Visibility.INHERITED : overridingInfo.getVisibility();
// Check that (a) the property *can* be overridden, and
// (b) that the visibility of the override is the same as the
// visibility of the original property.
if (visibility == Visibility.PRIVATE && !sameInput) {
compiler.report(
t.makeError(getprop, PRIVATE_OVERRIDE,
objectType.toString()));
} else if (overridingVisibility != Visibility.INHERITED &&
overridingVisibility != visibility) {
compiler.report(
t.makeError(getprop, VISIBILITY_MISMATCH,
visibility.name(), objectType.toString(),
overridingVisibility.name()));
}
} else {
if (sameInput) {
// private access is always allowed in the same file.
return;
} else if (visibility == Visibility.PRIVATE &&
(currentClass == null || ownerType.differsFrom(currentClass))) {
if (docInfo.isConstructor() &&
isValidPrivateConstructorAccess(parent)) {
return;
}
// private access is not allowed outside the file from a different
// enclosing class.
compiler.report(
t.makeError(getprop,
BAD_PRIVATE_PROPERTY_ACCESS,
propertyName,
validator.getReadableJSTypeName(
getprop.getFirstChild(), true)));
} else if (visibility == Visibility.PROTECTED) {
// There are 3 types of legal accesses of a protected property:
// 1) Accesses in the same file
// 2) Overriding the property in a subclass
// 3) Accessing the property from inside a subclass
// The first two have already been checked for.
if (currentClass == null || !currentClass.isSubtype(ownerType)) {
compiler.report(
t.makeError(getprop, BAD_PROTECTED_PROPERTY_ACCESS,
propertyName,
validator.getReadableJSTypeName(
getprop.getFirstChild(), true)));
}
}
}
}
}
/**
* Whether the given access of a private constructor is legal.
*
* For
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> example,
* new PrivateCtor_(); // not legal
* PrivateCtor_.newInstance(); // legal
* x instanceof PrivateCtor_ // legal
*
* This is a weird special case, because our visibility system is inherited
* from Java, and JavaScript has no distinction between classes and
* constructors like Java does.
*
* We may want to revisit this if we decide to make the restrictions tighter.
*/
private static boolean isValidPrivateConstructorAccess(Node parent) {
return parent.getType() != Token.NEW;
}
/**
* Determines whether a deprecation warning should be emitted.
* @param t The current traversal.
* @param n The node which we are checking.
* @param parent The parent of the node which we are checking.
*/
private boolean shouldEmitDeprecationWarning(
NodeTraversal t, Node n, Node parent) {
// In the global scope, there are only two kinds of accesses that should
// be flagged for warnings:
// 1) Calls of deprecated functions and methods.
// 2) Instantiations of deprecated classes.
// For now, we just let everything else by.
if (t.inGlobalScope()) {
if (!((parent.getType() == Token.CALL && parent.getFirstChild() == n) ||
n.getType() == Token.NEW)) {
return false;
}
}
// We can always assign to a deprecated property, to keep it up to date.
if (n.getType() == Token.GETPROP && n == parent.getFirstChild() &&
NodeUtil.isAssignmentOp(parent)) {
return false;
}
return !canAccessDeprecatedTypes(t);
}
/**
* Returns whether it's currently ok to access deprecated names and
* properties.
*
* There are 3 exceptions when we're allowed to use a deprecated
* type or property:
* 1) When we're in a deprecated function.
* 2) When we're in a deprecated class.
* 3) When we're in a static method of a deprecated class.
*/
private boolean canAccessDeprecatedTypes(NodeTraversal t) {
Node scopeRoot = t.getScopeRoot();
Node scopeRootParent = scopeRoot.getParent();
return
// Case #1
(deprecatedDepth > 0) ||
// Case #
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> this to generate deps files. (We're only using it for
// symbol dependencies.)
DependencyInfo info = (new JsFileParser(errorManager)).parseFile(
getName(), getName(), getCode());
provides.addAll(info.getProvides());
requires.addAll(info.getRequires());
generatedDependencyInfoFromSource = true;
}
}
}
private static class DepsFinder {
private final List<String> provides = Lists.newArrayList();
private final List<String> requires = Lists.newArrayList();
private final CodingConvention codingConvention =
new ClosureCodingConvention();
void visitTree(Node n) {
visitSubtree(n, null);
}
void visitSubtree(Node n, Node parent) {
if (n.getType() == Token.CALL) {
String require =
codingConvention.extractClassNameIfRequire(n, parent);
if (require != null) {
requires.add(require);
}
String provide =
codingConvention.extractClassNameIfProvide(n, parent);
if (provide != null) {
provides.add(provide);
}
return;
} else if (parent != null &&
parent.getType() != Token.EXPR_RESULT &&
parent.getType() != Token.SCRIPT) {
return;
}
for (Node child = n.getFirstChild();
child != null; child = child.getNext()) {
visitSubtree(child, n);
}
}
}
/**
* Gets the source line for the indicated line number.
*
* @param lineNumber the line number, 1 being the first line of the file.
* @return The line indicated. Does not include the newline at the end
* of the file. Returns {@code null} if it does not exist,
* or if there was an IO exception.
*/
public String getLine(int lineNumber) {
return getSourceFile().getLine(lineNumber);
}
/**
* Get a region around the indicated line number. The exact definition of a
* region is implementation specific, but it must contain the line indicated
* by the line number. A region must not start or end by a carriage return.
*
* @param lineNumber the line number, 1 being the first line of the file.
* @return The line indicated. Returns {@code null} if
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>>
* <li>{@code qualified.name = function() ...}</li>
* <li>{@code var name2 = function name1() ...}</li>
* <li>{@code qualified.name2 = function name1() ...}</li>
* </ul>
* In two last cases with named anonymous functions, the second name is
* returned (the variable of qualified name).
*
* @param n a node whose type is {@link Token#FUNCTION}
* @return the function's name, or {@code null} if it has no name
*/
static String getFunctionName(Node n) {
Node parent = n.getParent();
String name = n.getFirstChild().getString();
switch (parent.getType()) {
case Token.NAME:
// var name = function() ...
// var name2 = function name1() ...
return parent.getString();
case Token.ASSIGN:
// qualified.name = function() ...
// qualified.name2 = function name1() ...
return parent.getFirstChild().getQualifiedName();
default:
// function name() ...
return name != null && name.length() != 0 ? name : null;
}
}
/**
* Returns true if this is an immutable value.
*/
static boolean isImmutableValue(Node n) {
switch (n.getType()) {
case Token.STRING:
case Token.NUMBER:
case Token.NULL:
case Token.TRUE:
case Token.FALSE:
case Token.VOID:
return true;
case Token.NEG:
return isImmutableValue(n.getFirstChild());
case Token.NAME:
String name = n.getString();
// We assume here that programs don't change the value of the keyword
// undefined to something other than the value undefined.
return "undefined".equals(name)
|| "Infinity".equals(name)
|| "NaN".equals(name);
}
return false;
}
/**
* Returns true if this is a literal value. We define a literal value
* as any node that evaluates to the same thing regardless of when or
* where it is evaluated. So /xyz/ and [3, 5] are literals, but
* function() { return a; } is not.
*/
static boolean isLiteralValue(Node n) {
// TODO(
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>nicksantos): Refine this function to catch more literals.
switch (n.getType()) {
case Token.ARRAYLIT:
case Token.OBJECTLIT:
case Token.REGEXP:
// Return true only if all children are const.
for (Node child = n.getFirstChild(); child != null;
child = child.getNext()) {
if (!isLiteralValue(child)) {
return false;
}
}
return true;
default:
return isImmutableValue(n);
}
}
/**
* Determines whether the given value may be assigned to a define.
*
* @param val The value being assigned.
* @param defines The list of names of existing defines.
*/
static boolean isValidDefineValue(Node val, Set<String> defines) {
switch (val.getType()) {
case Token.STRING:
case Token.NUMBER:
case Token.TRUE:
case Token.FALSE:
return true;
// Single operators are valid if the child is valid.
case Token.BITAND:
case Token.BITNOT:
case Token.BITOR:
case Token.BITXOR:
case Token.NOT:
case Token.NEG:
return isValidDefineValue(val.getFirstChild(), defines);
// Names are valid if and only if they are defines themselves.
case Token.NAME:
case Token.GETPROP:
if (val.isQualifiedName()) {
return defines.contains(val.getQualifiedName());
}
}
return false;
}
/**
* Returns whether this a BLOCK node with no children.
*
* @param block The node.
*/
static boolean isEmptyBlock(Node block) {
if (block.getType() != Token.BLOCK) {
return false;
}
for (Node n = block.getFirstChild(); n != null; n = n.getNext()) {
if (n.getType() != Token.EMPTY) {
return false;
}
}
return true;
}
/**
* A "simple" operator is one whose children are expressions,
* has no direct side-effects (unlike '+='), and has no
* conditional aspects (unlike '||').
*/
static boolean isSimpleOperatorType(int type) {
switch (type) {
case Token.ADD:
case Token.BIT
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>()) {
// other side-effect free statements and expressions
case Token.AND:
case Token.BLOCK:
case Token.EXPR_RESULT:
case Token.HOOK:
case Token.IF:
case Token.IN:
case Token.LP:
case Token.NUMBER:
case Token.OR:
case Token.THIS:
case Token.TRUE:
case Token.FALSE:
case Token.NULL:
case Token.STRING:
case Token.SWITCH:
case Token.TRY:
case Token.EMPTY:
break;
// Throws are by definition side effects
case Token.THROW:
return true;
case Token.OBJECTLIT:
case Token.ARRAYLIT:
case Token.REGEXP:
if (checkForNewObjects) {
return true;
}
break;
case Token.VAR: // empty var statement (no declaration)
case Token.NAME: // variable by itself
if (n.getFirstChild() != null)
return true;
break;
case Token.FUNCTION:
// Anonymous functions don't have side-effects, but named ones
// change the namespace. Therefore, we check if the function has
// a name. Either way, we don't need to check the children, since
// they aren't executed at declaration time.
//
return !isFunctionAnonymous(n);
case Token.NEW:
{
if (checkForNewObjects) {
return true;
}
// calls to constructors that have no side effects have the
// no side effect property set.
if (n.isNoSideEffectsCall()) {
break;
}
// certain constructors are certified side effect free
Node constructor = n.getFirstChild();
if (Token.NAME == constructor.getType()) {
String className = constructor.getString();
if (CONSTRUCTORS_WITHOUT_SIDE_EFFECTS.contains(className)) {
// loop below will see if the constructor parameters have
// side-effects
break;
}
} else {
// the constructor could also be an expression like
// new (useArray ? Object : Array)();
}
}
return true;
case Token.CALL:
// calls to functions that have no side effects have the no
// side effect property set.
if (n.isNoSideEffectsCall()) {
// loop below will see
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> if the function parameters have
// side-effects
break;
}
return true;
default:
if (isSimpleOperatorType(n.getType()))
break;
if (isAssignmentOp(n)) {
// Assignments will have side effects if
// a) The RHS has side effects, or
// b) The LHS has side effects, or
// c) A name on the LHS will exist beyond the life of this statement.
if (checkForStateChangeHelper(
n.getFirstChild(), checkForNewObjects) ||
checkForStateChangeHelper(
n.getLastChild(), checkForNewObjects)) {
return true;
}
Node current = n.getFirstChild();
for (;
current.getType() == Token.GETPROP ||
current.getType() == Token.GETELEM;
current = current.getFirstChild()) { }
return !(isLiteralValue(current) ||
current.getType() == Token.FUNCTION);
}
return true;
}
for (Node c = n.getFirstChild(); c != null; c = c.getNext()) {
if (checkForStateChangeHelper(c, checkForNewObjects)) {
return true;
}
}
return false;
}
/**
* Do calls to this constructor have side effects?
*
* @param callNode - construtor call node
*/
static boolean constructorCallHasSideEffects(Node callNode) {
Preconditions.checkArgument(
callNode.getType() == Token.NEW,
"Expected NEW node, got " + Token.name(callNode.getType()));
if (callNode.isNoSideEffectsCall()) {
return false;
}
Node nameNode = callNode.getFirstChild();
if (nameNode.getType() == Token.NAME &&
CONSTRUCTORS_WITHOUT_SIDE_EFFECTS.contains(nameNode.getString())) {
return false;
}
return true;
}
/**
* Returns true if calls to this function have side effects.
*
* @param callNode - function call node
*/
static boolean functionCallHasSideEffects(Node callNode) {
Preconditions.checkArgument(
callNode.getType() == Token.CALL,
"Expected CALL node, got " + Token.name(callNode.getType()));
if (callNode.isNoSideEffectsCall()) {
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>
return false;
}
Node nameNode = callNode.getFirstChild();
// Built-in functions with no side effects.
if (nameNode.getType() == Token.NAME) {
String name = nameNode.getString();
if (name.equals("String")) {
return false;
}
}
// Functions in the "Math" namespace have no side effects.
if (nameNode.getType() == Token.GETPROP &&
nameNode.getFirstChild().getType() == Token.NAME) {
String namespaceName = nameNode.getFirstChild().getString();
if (namespaceName.equals("Math")) {
return false;
}
}
return true;
}
/**
* Returns true if the current node's type implies side effects.
*
* This is a non-recursive version of the may have side effects
* check; used to check wherever the current node's type is one of
* the reason's why a subtree has side effects.
*/
static boolean nodeTypeMayHaveSideEffects(Node n) {
if (NodeUtil.isAssignmentOp(n)) {
return true;
}
switch(n.getType()) {
case Token.CALL:
case Token.DELPROP:
case Token.NEW:
case Token.DEC:
case Token.INC:
case Token.THROW:
return true;
case Token.NAME:
// A variable definition.
return n.hasChildren();
default:
return false;
}
}
/**
* @return Whether the tree can be affected by side-effects or
* has side-effects.
*/
static boolean canBeSideEffected(Node n) {
Set<String> emptySet = Collections.emptySet();
return canBeSideEffected(n, emptySet);
}
/**
* @param knownConstants A set of names known to be constant value at
* node 'n' (such as locals that are last written before n can execute).
* @return Whether the tree can be affected by side-effects or
* has side-effects.
*/
static boolean canBeSideEffected(Node n, Set<String> knownConstants) {
switch (n.getType()) {
case Token.CALL:
case Token.NEW:
// Function calls or constructor can reference changed values.
// TODO(
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>johnlenz): Add some mechanism for determining that functions
// are unaffected by side effects.
return true;
case Token.NAME:
// Non-constant names values may have been changed.
return !NodeUtil.isConstantName(n)
&& !knownConstants.contains(n.getString());
// Properties on constant NAMEs can still be side-effected.
case Token.GETPROP:
case Token.GETELEM:
return true;
case Token.FUNCTION:
// Anonymous functions definitions are not changed by side-effects,
// and named functions are not part of expressions.
Preconditions.checkState(NodeUtil.isAnonymousFunction(n));
return false;
}
for (Node c = n.getFirstChild(); c != null; c = c.getNext()) {
if (canBeSideEffected(c, knownConstants)) {
return true;
}
}
return false;
}
/*
* 0 comma ,
* 1 assignment = += -= *= /= %= <<= >>= >>>= &= ^= |=
* 2 conditional ?:
* 3 logical-or ||
* 4 logical-and &&
* 5 bitwise-or |
* 6 bitwise-xor ^
* 7 bitwise-and &
* 8 equality == !=
* 9 relational < <= > >=
* 10 bitwise shift << >> >>>
* 11 addition/subtraction + -
* 12 multiply/divide * / %
* 13 negation/increment ! ~ - ++ --
* 14 call, member () [] .
*/
static int precedence(int type) {
switch (type) {
case Token.COMMA: return 0;
case Token.ASSIGN_BITOR:
case Token.ASSIGN_BITXOR:
case Token.ASSIGN_BITAND:
case Token.ASSIGN_LSH:
case Token.ASSIGN_RSH:
case Token.ASSIGN_URSH:
case Token.ASSIGN_ADD:
case Token.ASSIGN_SUB:
case Token.ASSIGN_MUL:
case Token.ASSIGN_DIV:
case Token.ASSIGN_MOD:
case Token.ASSIGN: return 1;
case Token.HOOK: return 2; // ?: operator
case Token.OR: return
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> node?
*/
static boolean isGet(Node n) {
return n.getType() == Token.GETPROP
|| n.getType() == Token.GETELEM;
}
/**
* Is this a GETPROP node?
*/
static boolean isGetProp(Node n) {
return n.getType() == Token.GETPROP;
}
/**
* Is this a NAME node?
*/
static boolean isName(Node n) {
return n.getType() == Token.NAME;
}
/**
* Is this a NEW node?
*/
static boolean isNew(Node n) {
return n.getType() == Token.NEW;
}
/**
* Is this a VAR node?
*/
static boolean isVar(Node n) {
return n.getType() == Token.VAR;
}
/**
* Is this node the name of a variable being declared?
*
* @param n The node
* @return True if {@code n} is NAME and {@code parent} is VAR
*/
static boolean isVarDeclaration(Node n) {
// There is no need to verify that parent != null because a NAME node
// always has a parent in a valid parse tree.
return n.getType() == Token.NAME && n.getParent().getType() == Token.VAR;
}
/**
* For an assignment or variable declaration get the assigned value.
* @return The value node representing the new value.
*/
static Node getAssignedValue(Node n) {
Preconditions.checkState(isName(n));
Node parent = n.getParent();
if (isVar(parent)) {
return n.getFirstChild();
} else if (isAssign(parent) && parent.getFirstChild() == n) {
return n.getNext();
} else {
return null;
}
}
/**
* Is this a STRING node?
*/
static boolean isString(Node n) {
return n.getType() == Token.STRING;
}
/**
* Is this node an assignment expression statement?
*
* @param n The node
* @return True if {@code n} is EXPR_RESULT and {@code n}'s
* first child is ASSIGN
*/
static boolean isExprAssign(Node n) {
return n.getType() == Token
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>.EXPR_RESULT
&& n.getFirstChild().getType() == Token.ASSIGN;
}
/**
* Is this an ASSIGN node?
*/
static boolean isAssign(Node n) {
return n.getType() == Token.ASSIGN;
}
/**
* Is this node a call expression statement?
*
* @param n The node
* @return True if {@code n} is EXPR_RESULT and {@code n}'s
* first child is CALL
*/
static boolean isExprCall(Node n) {
return n.getType() == Token.EXPR_RESULT
&& n.getFirstChild().getType() == Token.CALL;
}
/**
* @return Whether the node represents a FOR-IN loop.
*/
static boolean isForIn(Node n) {
return n.getType() == Token.FOR
&& n.getChildCount() == 3;
}
/**
* Determines whether the given node is a FOR, DO, or WHILE node.
*/
static boolean isLoopStructure(Node n) {
switch (n.getType()) {
case Token.FOR:
case Token.DO:
case Token.WHILE:
return true;
default:
return false;
}
}
/**
* @param n The node to inspect.
* @return If the node, is a FOR, WHILE, or DO, it returns the node for
* the code BLOCK, null otherwise.
*/
static Node getLoopCodeBlock(Node n) {
switch (n.getType()) {
case Token.FOR:
case Token.WHILE:
return n.getLastChild();
case Token.DO:
return n.getFirstChild();
default:
return null;
}
}
/**
* Determines whether the given node is a FOR, DO, WHILE, WITH, or IF node.
*/
static boolean isControlStructure(Node n) {
switch (n.getType()) {
case Token.FOR:
case Token.DO:
case Token.WHILE:
case Token.WITH:
case Token.IF:
case Token.LABEL:
case Token.TRY:
case Token.CATCH:
case Token.SWITCH:
case Token.CASE:
case Token.DEFAULT:
return true;
default:
return false;
}
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> }
/**
* Determines whether the given node is code node for FOR, DO,
* WHILE, WITH, or IF node.
*/
static boolean isControlStructureCodeBlock(Node parent, Node n) {
switch (parent.getType()) {
case Token.FOR:
case Token.WHILE:
case Token.LABEL:
case Token.WITH:
return parent.getLastChild() == n;
case Token.DO:
return parent.getFirstChild() == n;
case Token.IF:
return parent.getFirstChild() != n;
case Token.TRY:
return parent.getFirstChild() == n || parent.getLastChild() == n;
case Token.CATCH:
return parent.getLastChild() == n;
case Token.SWITCH:
case Token.CASE:
return parent.getFirstChild() != n;
case Token.DEFAULT:
return true;
default:
Preconditions.checkState(isControlStructure(parent));
return false;
}
}
/**
* Gets the condition of an ON_TRUE / ON_FALSE CFG edge.
* @param n a node with an outgoing conditional CFG edge
* @return the condition node or null if the condition is not obviously a node
*/
static Node getConditionExpression(Node n) {
switch (n.getType()) {
case Token.IF:
case Token.WHILE:
return n.getFirstChild();
case Token.DO:
return n.getLastChild();
case Token.FOR:
switch (n.getChildCount()) {
case 3:
return null;
case 4:
return n.getFirstChild().getNext();
}
throw new IllegalArgumentException("malformed 'for' statement " + n);
case Token.CASE:
return null;
}
throw new IllegalArgumentException(n + " does not have a condition.");
}
/**
* @return Whether the node is of a type that contain other statements.
*/
static boolean isStatementBlock(Node n) {
return n.getType() == Token.SCRIPT || n.getType() == Token.BLOCK;
}
/**
* @return Whether the node is used as a statement.
*/
static boolean isStatement(Node n) {
Node parent = n.getParent();
// It is not possible to determine definitely if a node is a statement
// or
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> not if it is not part of the AST. A FUNCTION node, for instance,
// is either part of an expression (as a anonymous function) or as
// a statement.
Preconditions.checkState(parent != null);
switch (parent.getType()) {
case Token.SCRIPT:
case Token.BLOCK:
case Token.LABEL:
return true;
default:
return false;
}
}
/** Whether the node is part of a switch statement. */
static boolean isSwitchCase(Node n) {
return n.getType() == Token.CASE || n.getType() == Token.DEFAULT;
}
/**
* @return Whether the name is a reference to a variable, function or
* function parameter (not a label or a empty anonymous function name).
*/
static boolean isReferenceName(Node n) {
return isName(n) && !n.getString().isEmpty();
}
/** @return Whether the node is a label name. */
static boolean isLabelName(Node n) {
return (n != null && n.getType() == Token.LABEL_NAME);
}
/** Whether the child node is the FINALLY block of a try. */
static boolean isTryFinallyNode(Node parent, Node child) {
return parent.getType() == Token.TRY && parent.getChildCount() == 3
&& child == parent.getLastChild();
}
/** Safely remove children while maintaining a valid node structure. */
static void removeChild(Node parent, Node node) {
// Node parent = node.getParent();
if (isStatementBlock(parent)
|| isSwitchCase(node)
|| isTryFinallyNode(parent, node)) {
// A statement in a block can simply be removed.
parent.removeChild(node);
} else if (parent.getType() == Token.VAR) {
if (parent.hasMoreThanOneChild()) {
parent.removeChild(node);
} else {
// Remove the node from the parent, so it can be reused.
parent.removeChild(node);
// This would leave an empty VAR, remove the VAR itself.
removeChild(parent.getParent(), parent);
}
} else if (node.getType() == Token.BLOCK) {
// Simply empty the block. This maintains source location and
// "synthetic"-
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>ness.
node.detachChildren();
} else if (parent.getType() == Token.LABEL
&& node == parent.getLastChild()) {
// Remove the node from the parent, so it can be reused.
parent.removeChild(node);
// A LABEL without children can not be referred to, remove it.
removeChild(parent.getParent(), parent);
} else if (parent.getType() == Token.FOR
&& parent.getChildCount() == 4) {
// Only Token.FOR can have an Token.EMPTY other control structure
// need something for the condition. Others need to be replaced
// or the structure removed.
parent.replaceChild(node, new Node(Token.EMPTY));
} else {
throw new IllegalStateException("Invalid attempt to remove node: " +
node.toString() + " of "+ parent.toString());
}
}
/**
* Merge a block with its parent block.
* @return Whether the block was removed.
*/
static boolean tryMergeBlock(Node block) {
Preconditions.checkState(block.getType() == Token.BLOCK);
Node parent = block.getParent();
// Try to remove the block if its parent is a block/script or if its
// parent is label and it has exactly one child.
if (NodeUtil.isStatementBlock(parent)) {
Node previous = block;
while (block.hasChildren()) {
Node child = block.removeFirstChild();
parent.addChildAfter(child, previous);
previous = child;
}
parent.removeChild(block);
return true;
} else if (parent.getType() == Token.LABEL && block.hasOneChild()) {
parent.replaceChild(block, block.removeFirstChild());
return true;
} else {
return false;
}
}
/**
* Is this a CALL node?
*/
static boolean isCall(Node n) {
return n.getType() == Token.CALL;
}
/**
* Is this a FUNCTION node?
*/
static boolean isFunction(Node n) {
return n.getType() == Token.FUNCTION;
}
/**
* Return a BLOCK node for the given FUNCTION node.
*/
static Node getFunctionBody(Node fn) {
Preconditions.checkArgument(isFunction(fn));
return fn.getLastChild();
}
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>
static boolean isFunctionAnonymous(Node n) {
return !isStatement(n);
}
/**
* Determines if a function takes a variable number of arguments by
* looking for references to the "arguments" var_args object.
*/
static boolean isVarArgsFunction(Node function) {
Preconditions.checkArgument(isFunction(function));
return NodeUtil.isNameReferenced(
function.getLastChild(),
"arguments",
Predicates.<Node>not(new NodeUtil.MatchNodeType(Token.FUNCTION)));
}
/**
* @return Whether node is a call to methodName.
* a.f(...)
* a['f'](...)
*/
static boolean isObjectCallMethod(Node callNode, String methodName) {
if (callNode.getType() == Token.CALL) {
Node functionIndentifyingExpression = callNode.getFirstChild();
if (NodeUtil.isGet(functionIndentifyingExpression)) {
Node last = functionIndentifyingExpression.getLastChild();
if (last != null && last.getType() == Token.STRING) {
String propName = last.getString();
return (propName.equals(methodName));
}
}
}
return false;
}
/**
* @return Whether the callNode represents an expression in the form of:
* x.call(...)
* x['call'](...)
*/
static boolean isFunctionObjectCall(Node callNode) {
return isObjectCallMethod(callNode, "call");
}
/**
* @return Whether the callNode represents an expression in the form of:
* x.apply(...)
* x['apply'](...)
*/
static boolean isFunctionObjectApply(Node callNode) {
return isObjectCallMethod(callNode, "apply");
}
/**
* @return Whether the callNode represents an expression in the form of:
* x.call(...)
* x['call'](...)
* where x is a NAME node.
*/
static boolean isSimpleFunctionObjectCall(Node callNode) {
if (isFunctionObjectCall(callNode)) {
if (callNode.getFirstChild().getFirstChild().getType() == Token.NAME) {
return true;
}
}
return false;
}
/**
* Determines whether this node is strictly on the left hand side of
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> an assign
* or var initialization. Notably, this does not include all L-values, only
* statements where the node is used only as an L-value.
*
* @param n The node
* @param parent Parent of the node
* @return True if n is the left hand of an assign
*/
static boolean isLhs(Node n, Node parent) {
return (parent.getType() == Token.ASSIGN && parent.getFirstChild() == n) ||
parent.getType() == Token.VAR;
}
/**
* Determines whether a node represents an object literal key
* (e.g. key1 in {key1: value1, key2: value2}).
*
* @param node A node
* @param parent The node's parent
*/
static boolean isObjectLitKey(Node node, Node parent) {
if (node.getType() == Token.STRING && parent.getType() == Token.OBJECTLIT) {
int index = 0;
for (Node current = parent.getFirstChild();
current != null;
current = current.getNext()) {
if (current == node) {
return index % 2 == 0;
}
index++;
}
}
return false;
}
/**
* Converts an operator's token value (see {@link Token}) to a string
* representation.
*
* @param operator the operator's token value to convert
* @return the string representation or {@code null} if the token value is
* not an operator
*/
static String opToStr(int operator) {
switch (operator) {
case Token.BITOR: return "|";
case Token.OR: return "||";
case Token.BITXOR: return "^";
case Token.AND: return "&&";
case Token.BITAND: return "&";
case Token.SHEQ: return "===";
case Token.EQ: return "==";
case Token.NOT: return "!";
case Token.NE: return "!=";
case Token.SHNE: return "!==";
case Token.LSH: return "<<";
case Token.IN: return "in";
case Token.LE: return "<=";
case Token.LT: return "<";
case Token.URSH: return ">>>";
case Token
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>
*/
static boolean containsType(Node node,
int type,
Predicate<Node> traverseChildrenPred) {
return has(node, new MatchNodeType(type), traverseChildrenPred);
}
/**
* @return true if n or any of its children are of the specified type
*/
static boolean containsType(Node node, int type) {
return containsType(node, type, Predicates.<Node>alwaysTrue());
}
/**
* Given a node tree, finds all the VAR declarations in that tree that are
* not in an inner scope. Then adds a new VAR node at the top of the current
* scope that redeclares them, if necessary.
*/
static void redeclareVarsInsideBranch(Node branch) {
Collection<Node> vars = getVarsDeclaredInBranch(branch);
if (vars.isEmpty()) {
return;
}
Node parent = getAddingRoot(branch);
for (Node nameNode : vars) {
Node var = new Node(
Token.VAR, Node.newString(Token.NAME, nameNode.getString()));
copyNameAnnotations(nameNode, var.getFirstChild());
parent.addChildToFront(var);
}
}
/**
* Copy any annotations that follow a named value.
* @param source
* @param destination
*/
static void copyNameAnnotations(Node source, Node destination) {
if (source.getBooleanProp(Node.IS_CONSTANT_NAME)) {
destination.putBooleanProp(Node.IS_CONSTANT_NAME, true);
}
}
/**
* Gets a Node at the top of the current scope where we can add new var
* declarations as children.
*/
private static Node getAddingRoot(Node n) {
Node addingRoot = null;
Node ancestor = n;
while (null != (ancestor = ancestor.getParent())) {
int type = ancestor.getType();
if (type == Token.SCRIPT) {
addingRoot = ancestor;
break;
} else if (type == Token.FUNCTION) {
addingRoot = ancestor.getLastChild();
break;
}
}
// make sure that the adding root looks ok
Preconditions.checkState(addingRoot.getType() == Token.BLOCK ||
addingRoot.getType() == Token.SCRIPT);
Preconditions.checkState(addingRoot.getFirstChild()
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> == null ||
addingRoot.getFirstChild().getType() != Token.SCRIPT);
return addingRoot;
}
/** Creates function name(params_0, ..., params_n) { body }. */
public static FunctionNode newFunctionNode(String name, List<Node> params,
Node body, int lineno, int charno) {
Node parameterParen = new Node(Token.LP, lineno, charno);
for (Node param : params) {
parameterParen.addChildToBack(param);
}
FunctionNode function = new FunctionNode(name, lineno, charno);
function.addChildrenToBack(
Node.newString(Token.NAME, name, lineno, charno));
function.addChildToBack(parameterParen);
function.addChildToBack(body);
return function;
}
/**
* Creates a node representing a qualified name.
*
* @param name A qualified name (e.g. "foo" or "foo.bar.baz")
* @param lineno The source line offset.
* @param charno The source character offset from start of the line.
* @return A NAME or GETPROP node
*/
public static Node newQualifiedNameNode(String name, int lineno, int charno) {
int endPos = name.indexOf('.');
if (endPos == -1) {
return Node.newString(Token.NAME, name, lineno, charno);
}
Node node = Node.newString(Token.NAME, name.substring(0, endPos),
lineno, charno);
int startPos;
do {
startPos = endPos + 1;
endPos = name.indexOf('.', startPos);
String part = (endPos == -1
? name.substring(startPos)
: name.substring(startPos, endPos));
node = new Node(Token.GETPROP, node,
Node.newString(Token.STRING, part, lineno, charno),
lineno, charno);
} while (endPos != -1);
return node;
}
/**
* Creates a node representing a qualified name, copying over the source
* location information from the basis node and assigning the given original
* name to the node.
*
* @param name A qualified name (e.g. "foo" or "foo.
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> {
vars.put(name, n);
}
}
}
}
}
/**
* Retrieves vars declared in the current node tree, excluding descent scopes.
*/
public static Collection<Node> getVarsDeclaredInBranch(Node root) {
VarCollector collector = new VarCollector();
visitPreOrder(
root,
collector,
Predicates.<Node>not(new NodeUtil.MatchNodeType(Token.FUNCTION)));
return collector.vars.values();
}
/**
* @return {@code true} if the node an assignment to a prototype property of
* some constructor.
*/
static boolean isPrototypePropertyDeclaration(Node n) {
if (!NodeUtil.isExprAssign(n)) {
return false;
}
return isPrototypeProperty(n.getFirstChild().getFirstChild());
}
static boolean isPrototypeProperty(Node n) {
String lhsString = n.getQualifiedName();
if (lhsString == null) {
return false;
}
int prototypeIdx = lhsString.indexOf(".prototype.");
return prototypeIdx != -1;
}
/**
* @return The class name part of a qualified prototype name.
*/
static Node getPrototypeClassName(Node qName) {
Node cur = qName;
while (isGetProp(cur)) {
if (cur.getLastChild().getString().equals("prototype")) {
return cur.getFirstChild();
} else {
cur = cur.getFirstChild();
}
}
return null;
}
/**
* @return The string property name part of a qualified prototype name.
*/
static String getPrototypePropertyName(Node qName) {
String qNameStr = qName.getQualifiedName();
int prototypeIdx = qNameStr.lastIndexOf(".prototype.");
int memberIndex = prototypeIdx + ".prototype".length() + 1;
return qNameStr.substring(memberIndex);
}
/**
* Create a node for an empty result expression:
* "void 0"
*/
static Node newUndefinedNode() {
// TODO(johnlenz): Why this instead of the more common "undefined"?
return new Node(Token.VOID, Node.newNumber(0));
}
/**
* Create a VAR node containing the given name and initial value expression.
*/
static Node newVarNode(
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>(Node node, String name) {
return getCount(node, new MatchNameNode(name) );
}
/**
* @return Whether the predicate is true for the node or any of its children.
*/
static boolean has(Node node,
Predicate<Node> pred,
Predicate<Node> traverseChildrenPred) {
if (pred.apply(node)) {
return true;
}
if (!traverseChildrenPred.apply(node)) {
return false;
}
for (Node c = node.getFirstChild(); c != null; c = c.getNext()) {
if (has(c, pred, traverseChildrenPred)) {
return true;
}
}
return false;
}
/**
* @return The number of times the the predicate is true for the node
* or any of its children.
*/
static int getCount(Node n, Predicate<Node> pred) {
int total = 0;
if (pred.apply(n)) {
total++;
}
for (Node c = n.getFirstChild(); c != null; c = c.getNext()) {
total += getCount(c, pred);
}
return total;
}
/**
* Interface for use with the visit method.
* @see #visit
*/
static interface Visitor {
void visit(Node node);
}
/**
* A pre-order traversal, calling Vistor.visit for each child matching
* the predicate.
*/
static void visitPreOrder(Node node,
Visitor vistor,
Predicate<Node> traverseChildrenPred) {
vistor.visit(node);
if (traverseChildrenPred.apply(node)) {
for (Node c = node.getFirstChild(); c != null; c = c.getNext()) {
visitPreOrder(c, vistor, traverseChildrenPred);
}
}
}
/**
* A post-order traversal, calling Vistor.visit for each child matching
* the predicate.
*/
static void visitPostOrder(Node node,
Visitor vistor,
Predicate<Node> traverseChildrenPred) {
if (traverseChildrenPred.apply(node)) {
for (Node c = node.getFirstChild(); c != null; c = c.getNext()) {
visitPostOrder(c, vistor, traverseChildrenPred);
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> }
}
vistor.visit(node);
}
/**
* @return Whether a TRY node has a finally block.
*/
static boolean hasFinally(Node n) {
Preconditions.checkArgument(n.getType() == Token.TRY);
return n.getChildCount() == 3;
}
/**
* @return The BLOCK node containing the CATCH node (if any)
* of a TRY.
*/
static Node getCatchBlock(Node n) {
Preconditions.checkArgument(n.getType() == Token.TRY);
return n.getFirstChild().getNext();
}
/**
* @return Whether BLOCK (from a TRY node) contains a CATCH.
* @see NodeUtil#getCatchBlock
*/
static boolean hasCatchHandler(Node n) {
Preconditions.checkArgument(n.getType() == Token.BLOCK);
return n.hasChildren() && n.getFirstChild().getType() == Token.CATCH;
}
/**
* @param fnNode The function.
* @return The Node containing the Function parameters.
*/
static Node getFnParameters(Node fnNode) {
// Function NODE: [ FUNCTION -> NAME, LP -> ARG1, ARG2, ... ]
Preconditions.checkArgument(fnNode.getType() == Token.FUNCTION);
return fnNode.getFirstChild().getNext();
}
/**
* Returns true if a name node represents a constant variable.
*
* <p>Determining whether a variable is constant has three steps:
* <ol>
* <li>In CodingConventionAnnotator, any name that matches the
* {@link CodingConvention#isConstant(String)} is annotated with an
* IS_CONSTANT_NAME property.
* <li>The normalize pass renames any variable with the IS_CONSTANT_NAME
* annotation and that is initialized to a constant value with
* a variable name inlucding $$constant.
* <li>Return true here if the variable includes $$constant in its name.
* </ol>
*
* @param node A NAME or STRING node
* @return True if the variable is constant
*/
static boolean isConstantName(Node node) {
return node.getBooleanProp(Node.IS_CONSTANT_NAME);
}
/**
* @param nameNode A name node
* @
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> path through
// the code identical to how it's been for years.
this.outputCharsetEncoder = null;
} else {
this.outputCharsetEncoder = outputCharset.newEncoder();
}
}
CodeGenerator(CodeConsumer consumer, Charset outputCharset) {
this(consumer, outputCharset, true);
}
CodeGenerator(CodeConsumer consumer) {
this(consumer, null, false);
}
void add(String str) {
cc.add(str);
}
private void addIdentifier(String identifier) {
cc.addIdentifier(identifierEscape(identifier));
}
void add(Node n) {
add(n, Context.OTHER);
}
void add(Node n, Context context) {
if (!cc.continueProcessing()) {
return;
}
int type = n.getType();
String opstr = NodeUtil.opToStr(type);
int childCount = n.getChildCount();
Node first = n.getFirstChild();
Node last = n.getLastChild();
// Handle all binary operators
if (opstr != null && first != last) {
Preconditions.checkState(childCount == 2);
int p = NodeUtil.precedence(type);
addLeftExpr(first, p, context);
cc.addOp(opstr, true);
// For right-hand-side of operations, only pass context if it's
// the IN_FOR_INIT_CLAUSE one.
Context rhsContext = getContextForNoInOperator(context);
// Handle associativity.
// e.g. if the parse tree is a * (b * c),
// we can simply generate a * b * c.
if (last.getType() == type &&
NodeUtil.isAssociative(type)) {
addExpr(last, p, rhsContext);
} else if (NodeUtil.isAssignmentOp(n) && NodeUtil.isAssignmentOp(last)) {
// Assignments are the only right-associative binary operators
addExpr(last, p, rhsContext);
} else {
addExpr(last, p + 1, rhsContext);
}
return;
}
cc.startSourceMapping(n);
switch (type) {
case Token.TRY: {
Preconditions.checkState(first.getNext().getType() ==
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> Token.BLOCK &&
first.getNext().getChildCount() <= 1);
Preconditions.checkState(childCount >= 2 && childCount <= 3);
add("try");
add(first, Context.PRESERVE_BLOCK);
// second child contains the catch block, or nothing if there
// isn't a catch block
Node catchblock = first.getNext().getFirstChild();
if (catchblock != null) {
add(catchblock);
}
if (childCount == 3) {
add("finally");
add(last, Context.PRESERVE_BLOCK);
}
break;
}
case Token.CATCH:
Preconditions.checkState(childCount == 3);
if (first.getNext().getType() != Token.EMPTY) {
throw new Error("Catch conditions not suppored because I think" +
" that it may be a netscape only feature.");
}
add("catch(");
add(first);
add(")");
add(last, Context.PRESERVE_BLOCK);
break;
case Token.THROW:
Preconditions.checkState(childCount == 1);
add("throw");
add(first);
// Must have a ';' after a throw statement, otherwise safari can't
// parse this.
cc.endStatement(true);
break;
case Token.RETURN:
add("return");
if (childCount == 1) {
add(first);
} else {
Preconditions.checkState(childCount == 0);
}
cc.endStatement();
break;
case Token.VAR:
if (first != null) {
add("var ");
addList(first, false, getContextForNoInOperator(context));
}
break;
case Token.LABEL_NAME:
Preconditions.checkState(!n.getString().isEmpty());
addIdentifier(n.getString());
break;
case Token.NAME:
if (first == null || first.getType() == Token.EMPTY) {
addIdentifier(n.getString());
} else {
Preconditions.checkState(childCount == 1);
addIdentifier(n.getString());
cc.addOp("=", true);
if (first.getType() == Token.COMMA) {
addExpr(first, NodeUtil.precedence(Token.ASSIGN));
} else
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> {
// Add expression, consider nearby code at lowest level of
// precedence.
addExpr(first, 0, getContextForNoInOperator(context));
}
}
break;
case Token.ARRAYLIT:
add("[");
addList(first, (int[]) n.getProp(Node.SKIP_INDEXES_PROP));
add("]");
break;
case Token.LP:
add("(");
addList(first);
add(")");
break;
case Token.COMMA:
addList(first, false, context);
break;
case Token.NUMBER:
Preconditions.checkState(childCount == 0);
cc.addNumber(n.getDouble());
break;
case Token.TYPEOF:
case Token.VOID:
case Token.NOT:
case Token.BITNOT:
case Token.POS:
case Token.NEG: {
// All of these unary operators are right-associative
Preconditions.checkState(childCount == 1);
cc.addOp(NodeUtil.opToStrNoFail(type), false);
addExpr(first, NodeUtil.precedence(type));
break;
}
case Token.HOOK: {
Preconditions.checkState(childCount == 3);
int p = NodeUtil.precedence(type);
addLeftExpr(first, p + 1, context);
cc.addOp("?", true);
addExpr(first.getNext(), p);
cc.addOp(":", true);
addExpr(last, p);
break;
}
case Token.REGEXP:
if (first.getType() != Token.STRING ||
last.getType() != Token.STRING) {
throw new Error("Expected children to be strings");
}
String regexp = regexpEscape(first.getString(), outputCharsetEncoder);
// I only use one .add because whitespace matters
if (childCount == 2) {
add(regexp + last.getString());
} else {
Preconditions.checkState(childCount == 1);
add(regexp);
}
break;
case Token.GET_REF:
add(first);
break;
case Token.REF_SPECIAL:
Preconditions.checkState(childCount == 1);
add(first);
add(".");
add((String) n.
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>getProp(Node.NAME_PROP));
break;
case Token.FUNCTION:
Preconditions.checkState(childCount == 3);
boolean funcNeedsParens = (context == Context.START_OF_EXPR);
if (funcNeedsParens) {
add("(");
}
add("function");
add(first);
add(first.getNext());
add(last, Context.PRESERVE_BLOCK);
cc.endFunction(context == Context.STATEMENT);
if (funcNeedsParens) {
add(")");
}
break;
case Token.SCRIPT:
case Token.BLOCK: {
boolean stripBlock = n.isSyntheticBlock() ||
((context != Context.PRESERVE_BLOCK) && (n.getChildCount() < 2));
if (!stripBlock) {
cc.beginBlock();
}
for (Node c = first; c != null; c = c.getNext()) {
add(c, Context.STATEMENT);
// VAR doesn't include ';' since it gets used in expressions
if (c.getType() == Token.VAR) {
cc.endStatement();
}
if (c.getType() == Token.FUNCTION) {
cc.maybeLineBreak();
}
// Prefer to break lines in between top-level statements
// because top level statements are more homogeneous.
if (type == Token.SCRIPT) {
cc.notePreferredLineBreak();
}
}
if (!stripBlock) {
cc.endBlock(cc.breakAfterBlockFor(n, context == Context.STATEMENT));
}
break;
}
case Token.FOR:
if (childCount == 4) {
add("for(");
if (first.getType() == Token.VAR) {
add(first, Context.IN_FOR_INIT_CLAUSE);
} else {
addExpr(first, 0, Context.IN_FOR_INIT_CLAUSE);
}
add(";");
add(first.getNext());
add(";");
add(first.getNext().getNext());
add(")");
addNonEmptyExpression(
last, getContextForNonEmptyExpression(context), false);
} else {
Preconditions.checkState(childCount == 3);
add("for(");
add(first
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>);
add("in");
add(first.getNext());
add(")");
addNonEmptyExpression(
last, getContextForNonEmptyExpression(context), false);
}
break;
case Token.DO:
Preconditions.checkState(childCount == 2);
add("do");
addNonEmptyExpression(first, Context.OTHER, false);
add("while(");
add(last);
add(")");
cc.endStatement();
break;
case Token.WHILE:
Preconditions.checkState(childCount == 2);
add("while(");
add(first);
add(")");
addNonEmptyExpression(
last, getContextForNonEmptyExpression(context), false);
break;
case Token.EMPTY:
Preconditions.checkState(childCount == 0);
break;
case Token.GETPROP: {
Preconditions.checkState(childCount == 2);
Preconditions.checkState(last.getType() == Token.STRING);
boolean needsParens = (first.getType() == Token.NUMBER);
if (needsParens) {
add("(");
}
addLeftExpr(first, NodeUtil.precedence(type), context);
if (needsParens) {
add(")");
}
add(".");
addIdentifier(last.getString());
break;
}
case Token.GETELEM:
Preconditions.checkState(childCount == 2);
addLeftExpr(first, NodeUtil.precedence(type), context);
add("[");
add(first.getNext());
add("]");
break;
case Token.WITH:
Preconditions.checkState(childCount == 2);
add("with(");
add(first);
add(")");
addNonEmptyExpression(
last, getContextForNonEmptyExpression(context), false);
break;
case Token.INC:
case Token.DEC: {
Preconditions.checkState(childCount == 1);
String o = type == Token.INC ? "++" : "--";
int postProp = n.getIntProp(Node.INCRDECR_PROP, 0);
// A non-zero post-prop value indicates a post inc/dec, default of zero
// is a pre-inc/dec.
if (postProp != 0) {
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> addLeftExpr(first, NodeUtil.precedence(type), context);
cc.addOp(o, false);
} else {
cc.addOp(o, false);
add(first);
}
break;
}
case Token.CALL:
// If the left hand side of the call is a direct reference to eval,
// then it must have a DIRECT_EVAL annotation. If it does not, then
// that means it was originally an indirect call to eval, and that
// indirectness must be preserved.
if (first.getType() == Token.NAME &&
"eval".equals(first.getString()) &&
!first.getBooleanProp(Node.DIRECT_EVAL)) {
add("(0,eval)");
} else {
addLeftExpr(first, NodeUtil.precedence(type), context);
}
add("(");
addList(first.getNext());
add(")");
break;
case Token.IF:
boolean hasElse = childCount == 3;
boolean ambiguousElseClause =
context == Context.BEFORE_DANGLING_ELSE && !hasElse;
if (ambiguousElseClause) {
cc.beginBlock();
}
add("if(");
add(first);
add(")");
if (hasElse) {
addNonEmptyExpression(
first.getNext(), Context.BEFORE_DANGLING_ELSE, false);
add("else");
addNonEmptyExpression(
last, getContextForNonEmptyExpression(context), false);
} else {
addNonEmptyExpression(first.getNext(), Context.OTHER, false);
Preconditions.checkState(childCount == 2);
}
if (ambiguousElseClause) {
cc.endBlock();
}
break;
case Token.NULL:
case Token.THIS:
case Token.FALSE:
case Token.TRUE:
Preconditions.checkState(childCount == 0);
add(Node.tokenToName(type));
break;
case Token.CONTINUE:
Preconditions.checkState(childCount <= 1);
add("continue");
if (childCount == 1) {
if (first.getType() != Token.LABEL_NAME && validation) {
throw new Error("Unexpected token type. Should be LABEL_NAME.");
}
add(" ");
add(
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>first);
}
cc.endStatement();
break;
case Token.DEBUGGER:
Preconditions.checkState(childCount == 0);
add("debugger");
cc.endStatement();
break;
case Token.BREAK:
Preconditions.checkState(childCount <= 1);
add("break");
if (childCount == 1) {
if (first.getType() != Token.LABEL_NAME && validation) {
throw new Error("Unexpected token type. Should be LABEL_NAME.");
}
add(" ");
add(first);
}
cc.endStatement();
break;
case Token.EXPR_VOID:
case Token.EXPR_RESULT:
if (type == Token.EXPR_VOID && validation) {
throw new Error("Unexpected EXPR_VOID. Should be EXPR_RESULT.");
}
Preconditions.checkState(childCount == 1);
add(first, Context.START_OF_EXPR);
cc.endStatement();
break;
case Token.NEW:
add("new ");
int precedence = NodeUtil.precedence(type);
// If the first child contains a CALL, then claim higher precedence
// to force parens. Otherwise, when parsed, NEW will bind to the
// first viable parens
if (NodeUtil.containsCall(first)) {
precedence = NodeUtil.precedence(first.getType()) + 1;
}
addExpr(first, precedence);
// '()' is optional when no arguments are present
Node next = first.getNext();
if (next != null) {
add("(");
addList(next);
add(")");
}
break;
case Token.STRING:
Preconditions.checkState(childCount == 0);
add(jsString(n.getString(), outputCharsetEncoder));
break;
case Token.DELPROP:
Preconditions.checkState(childCount == 1);
add("delete ");
add(first);
break;
case Token.OBJECTLIT: {
Preconditions.checkState(childCount % 2 == 0);
boolean needsParens = (context == Context.START_OF_EXPR);
if (needsParens) {
add("(");
}
add("{");
for (Node c = first; c != null; c = c.getNext
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>().getNext()) {
if (c != first) {
cc.listSeparator();
}
// Object literal property names don't have to be quoted if they are
// not JavaScript keywords
if (c.getType() == Token.STRING &&
!TokenStream.isKeyword(c.getString()) &&
TokenStream.isJSIdentifier(c.getString()) &&
// do not encode literally any non-literal characters that were
// unicode escaped.
NodeUtil.isLatin(c.getString())) {
add(c.getString());
} else {
addExpr(c, 1);
}
add(":");
addExpr(c.getNext(), 1);
}
add("}");
if (needsParens) {
add(")");
}
break;
}
case Token.SWITCH:
add("switch(");
add(first);
add(")");
cc.beginBlock();
addAllSiblings(first.getNext());
cc.endBlock(context == Context.STATEMENT);
break;
case Token.CASE:
Preconditions.checkState(childCount == 2);
add("case ");
add(first);
addCaseBody(last);
break;
case Token.DEFAULT:
Preconditions.checkState(childCount == 1);
add("default");
addCaseBody(first);
break;
case Token.LABEL:
Preconditions.checkState(childCount == 2);
if (first.getType() != Token.LABEL_NAME && validation) {
throw new Error("Unexpected token type. Should be LABEL_NAME.");
}
add(first);
add(":");
addNonEmptyExpression(
last, getContextForNonEmptyExpression(context), true);
break;
// This node is auto generated in anonymous functions and should just get
// ignored for our purposes.
case Token.SETNAME:
break;
default:
throw new Error("Unknown type " + type + "\n" + n.toStringTree());
}
cc.endSourceMapping(n);
}
/**
* Adds a block or expression, substituting a VOID with an empty statement.
* This is used for "for (...);" and "if (...);" type statements.
*
* @param n The node to print.
* @param context The context to determine how the node should be
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> parsed as a block,
* but "{'x': 'y'}" is parsed as an object literal.
*/
void addLeftExpr(Node n, int minPrecedence, Context context) {
addExpr(n, minPrecedence, context);
}
void addExpr(Node n, int minPrecedence) {
addExpr(n, minPrecedence, Context.OTHER);
}
private void addExpr(Node n, int minPrecedence, Context context) {
if ((NodeUtil.precedence(n.getType()) < minPrecedence) ||
((context == Context.IN_FOR_INIT_CLAUSE) &&
(n.getType() == Token.IN))){
add("(");
add(n, clearContextForNoInOperator(context));
add(")");
} else {
add(n, context);
}
}
void addList(Node firstInList) {
addList(firstInList, true, Context.OTHER);
}
void addList(Node firstInList, boolean isArrayOrFunctionArgument) {
addList(firstInList, isArrayOrFunctionArgument, Context.OTHER);
}
void addList(Node firstInList, boolean isArrayOrFunctionArgument,
Context lhsContext) {
for (Node n = firstInList; n != null; n = n.getNext()) {
boolean isFirst = n == firstInList;
if (isFirst) {
addLeftExpr(n, isArrayOrFunctionArgument ? 1 : 0, lhsContext);
} else {
cc.listSeparator();
addExpr(n, isArrayOrFunctionArgument ? 1 : 0);
}
}
}
/**
* This function adds a comma-separated list as is specified by an ARRAYLIT
* node with the associated skipIndexes array. This is a space optimization
* since we avoid creating a whole Node object for each empty array literal
* slot.
* @param firstInList The first in the node list (chained through the next
* property).
* @param skipIndexes If not null, then the array of skipped entries in the
* array.
*/
void addList(Node firstInList, int[] skipIndexes) {
int nextSlot = 0;
int nextSkipSlot = 0;
for (Node n = first
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>InList; n != null; n = n.getNext()) {
while (skipIndexes != null && nextSkipSlot < skipIndexes.length) {
if (nextSlot == skipIndexes[nextSkipSlot]) {
cc.listSeparator();
nextSlot++;
nextSkipSlot++;
} else {
break;
}
}
if (n != firstInList) {
cc.listSeparator();
}
addExpr(n, 1);
nextSlot++;
}
}
void addCaseBody(Node caseBody) {
cc.beginCaseBody();
add(caseBody);
cc.endCaseBody();
}
void addAllSiblings(Node n) {
for (Node c = n; c != null; c = c.getNext()) {
add(c);
}
}
/** Outputs a js string, using the optimal (single/double) quote character */
static String jsString(String s, CharsetEncoder outputCharsetEncoder) {
int singleq = 0, doubleq = 0;
// could count the quotes and pick the optimal quote character
for (int i = 0; i < s.length(); i++) {
switch (s.charAt(i)) {
case '"': doubleq++; break;
case '\'': singleq++; break;
}
}
String doublequote, singlequote;
char quote;
if (singleq < doubleq) {
// more double quotes so escape the single quotes
quote = '\'';
doublequote = "\"";
singlequote = "\\\'";
} else {
// more single quotes so escape the doubles
quote = '\"';
doublequote = "\\\"";
singlequote = "\'";
}
return strEscape(s, quote, doublequote, singlequote, "\\\\",
outputCharsetEncoder);
}
/** Escapes regular expression */
static String regexpEscape(String s, CharsetEncoder outputCharsetEncoder) {
return strEscape(s, '/', "\"", "'", "\\", outputCharsetEncoder);
}
/**
* Escapes the given string to a double quoted (") JavaScript/JSON string
*/
static String escapeToDoubleQuotedJsString(String s) {
return strEscape(s, '"', "\\\"", "\'", "\\\\", null);
}
/* If the user doesn't want to specify an output
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>
// check is measurably faster than using the CharsetEncoder.
if (c > 0x1f && c <= 0x7f) {
sb.append(c);
} else {
// Other characters can be misinterpreted by some js parsers,
// or perhaps mangled by proxies along the way,
// so we play it safe and unicode escape them.
appendHexJavaScriptRepresentation(sb, c);
}
}
}
}
sb.append(quote);
return sb.toString();
}
static String identifierEscape(String s) {
// First check if escaping is needed at all -- in most cases it isn't.
if (NodeUtil.isLatin(s)) {
return s;
}
// Now going through the string to escape non-latin characters if needed.
StringBuilder sb = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
// Identifiers should always go to Latin1/ ASCII characters because
// different browser's rules for valid identifier characters are
// crazy.
if (c > 0x1F && c < 0x7F) {
sb.append(c);
} else {
appendHexJavaScriptRepresentation(sb, c);
}
}
return sb.toString();
}
/** Gets the number of children of this node that are non empty. */
private static int getNonEmptyChildCount(Node n) {
int i = 0;
for (Node c = n.getFirstChild(); c != null; c = c.getNext()) {
if (c.getType() != Token.EMPTY) {
i++;
}
}
return i;
}
/** Gets the first non-empty child of the given node. */
private static Node getFirstNonEmptyChild(Node n) {
for (Node c = n.getFirstChild(); c != null; c = c.getNext()) {
if (c.getType() != Token.EMPTY) {
return c;
}
}
return null;
}
// Information on the current context. Used for disambiguating special cases.
// For example, a "{" could indicate the start of an object literal or a
// block, depending on the current context.
enum
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> s = scope.getParent();
if (s == null) {
throw new IllegalArgumentException("Var is not local");
}
while (s.getParent() != null) {
num += s.getVarCount();
s = s.getParent();
}
return num;
}
/**
* Returns whether this is a global variable.
*/
public boolean isGlobal() {
return scope.isGlobal();
}
/**
* Returns whether this is a local variable.
*/
public boolean isLocal() {
return scope.isLocal();
}
/**
* Returns whether this is defined in an extern file.
*/
boolean isExtern() {
return input == null || input.isExtern();
}
/**
* Returns {@code true} if the variable is declared as a constant,
* based on the value reported by {@code NodeUtil}.
*/
public boolean isConst() {
return NodeUtil.isConstantName(nameNode);
}
/**
* Returns {@code true} if the variable is declared as a define.
* A variable is a define if it is annotaed by {@code @define}.
*/
public boolean isDefine() {
return isDefine;
}
public Node getInitialValue() {
Node parent = getParentNode();
return parent.getType() == Token.FUNCTION ?
parent : nameNode.getFirstChild();
}
/**
* Gets this variable's type. To know whether this type has been inferred,
* see {@code #isInferred()}.
*/
public JSType getType() {
return type;
}
/**
* Returns the name node that produced this variable.
*/
public Node getNameNode() {
return nameNode;
}
/**
* Gets the JSDocInfo for the variable.
*/
public JSDocInfo getJSDocInfo() {
return info;
}
/**
* Sets this variable's type.
* @throws IllegalStateException if the variable's type is not inferred
*/
void setType(JSType type) {
Preconditions.checkState(isTypeInferred());
this.type = type;
}
/**
* Returns whether this variable's type is inferred. To get the variable's
* type, see {@link #getType()}.
*/
public boolean isTypeInferred() {
return typeInferred;
}
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>() {
return isBottom;
}
/**
* Gets the container node of the scope. This is typically the FUNCTION
* node or the global BLOCK/SCRIPT node.
*/
public Node getRootNode() {
return rootNode;
}
public Scope getParent() {
return parent;
}
Scope getGlobalScope() {
Scope result = this;
while (result.getParent() != null) {
result = result.getParent();
}
return result;
}
@Override
public StaticScope<JSType> getParentScope() {
return parent;
}
/**
* Gets the type of {@code this} in the current scope.
*/
public ObjectType getTypeOfThis() {
return thisType;
}
/**
* Declares a variable whose type is inferred.
*
* @param name name of the variable
* @param nameNode the NAME node declaring the variable
* @param type the variable's type
* @param input the input in which this variable is defined.
*/
Var declare(String name, Node nameNode, JSType type, CompilerInput input) {
return declare(name, nameNode, type, input, true);
}
/**
* Declares a variable.
*
* @param name name of the variable
* @param nameNode the NAME node declaring the variable
* @param type the variable's type
* @param input the input in which this variable is defined.
* @param inferred Whether this variable's type is inferred (as opposed
* to declared).
*/
Var declare(String name, Node nameNode,
JSType type, CompilerInput input, boolean inferred) {
Preconditions.checkState(name != null && name.length() > 0);
// Make sure that it's declared only once
Preconditions.checkState(vars.get(name) == null);
Var var = new Var(inferred);
var.name = name;
var.nameNode = nameNode;
var.type = type;
var.scope = this;
var.index = vars.size();
var.input = input;
// native variables do not have a name node.
// TODO(user): make Var abstract and have NativeVar, NormalVar.
JSDocInfo info = NodeUtil.getInfoForNameNode(nameNode);
var
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>}).
*/
@Override
public SubclassRelationship getClassesDefinedByCall(Node callNode) {
Node callName = callNode.getFirstChild();
SubclassType type = typeofClassDefiningName(callName);
if (type != null) {
Node subclass = null;
Node superclass = callNode.getLastChild();
// There are six possible syntaxes for a class-defining method:
// SubClass.inherits(SuperClass)
// goog.inherits(SubClass, SuperClass)
// goog$inherits(SubClass, SuperClass)
// SubClass.mixin(SuperClass.prototype)
// goog.mixin(SubClass.prototype, SuperClass.prototype)
// goog$mixin(SubClass.prototype, SuperClass.prototype)
if (callNode.getChildCount() == 2 &&
callName.getType() == Token.GETPROP) {
// SubClass.inherits(SuperClass)
subclass = callName.getFirstChild();
} else if (callNode.getChildCount() == 3) {
// goog.inherits(SubClass, SuperClass)
subclass = callName.getNext();
}
// bail out if either of the side of the "inherits"
// isn't a real class name. This prevents us from
// doing something weird in cases like:
// goog.inherits(MySubClass, cond ? SuperClass1 : BaseClass2)
if (subclass != null &&
subclass.isUnscopedQualifiedName() &&
superclass.isUnscopedQualifiedName()) {
// make sure to strip the prototype off of the nodes
// to normalize for goog.mixin
return new SubclassRelationship(
type,
stripPrototype(subclass),
stripPrototype(superclass));
}
}
return null;
}
/**
* Determines whether the given node is a class-defining name, like
* "inherits" or "mixin."
* @return The type of class-defining name, or null.
*/
private SubclassType typeofClassDefiningName(Node callName) {
// Check if the method name matches one of the class-defining methods.
String methodName = null;
if (callName.getType() == Token.GETPROP) {
methodName = callName.getLastChild().getString();
} else if (callName.getType() == Token.NAME) {
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> String name = callName.getString();
int dollarIndex = name.lastIndexOf('$');
if (dollarIndex != -1) {
methodName = name.substring(dollarIndex + 1);
}
}
if (methodName != null) {
if (methodName.equals("inherits")) {
return SubclassType.INHERITS;
} else if (methodName.equals("mixin")) {
return SubclassType.MIXIN;
}
}
return null;
}
@Override
public boolean isSuperClassReference(String propertyName) {
return "superClass_".equals(propertyName);
}
/**
* Given a qualified name node, strip "prototype" off the end.
*
* Examples of this transformation:
* a.b.c => a.b.c
* a.b.c.prototype => a.b.c
*/
private Node stripPrototype(Node qualifiedName) {
if (qualifiedName.getType() == Token.GETPROP &&
qualifiedName.getLastChild().getString().equals("prototype")) {
return qualifiedName.getFirstChild();
}
return qualifiedName;
}
/**
* Exctracts X from goog.provide('X'), if the applied Node is goog.
*
* @return The extracted class name, or null.
*/
@Override
public String extractClassNameIfProvide(Node node, Node parent){
return extractClassNameIfGoog(node, parent, "goog.provide");
}
/**
* Exctracts X from goog.require('X'), if the applied Node is goog.
*
* @return The extracted class name, or null.
*/
@Override
public String extractClassNameIfRequire(Node node, Node parent){
return extractClassNameIfGoog(node, parent, "goog.require");
}
private static String extractClassNameIfGoog(Node node, Node parent,
String functionName){
String className = null;
if (NodeUtil.isExprCall(parent)) {
Node callee = node.getFirstChild();
if (callee != null && callee.getType() == Token.GETPROP) {
String qualifiedName = callee.getQualifiedName();
if ((functionName).equals(qualifiedName)) {
className = callee.getNext().getString();
}
}
}
return className;
}
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>
/**
* Use closure's implementation.
* @return closure's function name for exporting properties.
*/
@Override
public String getExportPropertyFunction() {
return "goog.exportProperty";
}
/**
* Use closure's implementation.
* @return closure's function name for exporting symbols.
*/
@Override
public String getExportSymbolFunction() {
return "goog.exportSymbol";
}
@Override
public List<String> identifyTypeDeclarationCall(Node n) {
Node callName = n.getFirstChild();
if ("goog.addDependency".equals(callName.getQualifiedName()) &&
n.getChildCount() >= 3) {
Node typeArray = callName.getNext().getNext();
if (typeArray.getType() == Token.ARRAYLIT) {
List<String> typeNames = Lists.newArrayList();
for (Node name = typeArray.getFirstChild(); name != null;
name = name.getNext()) {
if (name.getType() == Token.STRING) {
typeNames.add(name.getString());
}
}
return typeNames;
}
}
return null;
}
@Override
public String identifyTypeDefAssign(Node n) {
Node firstChild = n.getFirstChild();
int type = n.getType();
if (type == Token.ASSIGN) {
if (TYPEDEF_NAME.equals(n.getLastChild().getQualifiedName())) {
return firstChild.getQualifiedName();
}
} else if (type == Token.VAR && firstChild.hasChildren()) {
if (TYPEDEF_NAME.equals(
firstChild.getFirstChild().getQualifiedName())) {
return firstChild.getString();
}
}
return null;
}
@Override
public String getAbstractMethodName() {
return "goog.abstractMethod";
}
@Override
public String getSingletonGetterClassName(Node callNode) {
Node callName = callNode.getFirstChild();
if (!"goog.addSingletonGetter".equals(callName.getQualifiedName()) ||
callName.getChildCount() != 2) {
return null;
}
Node classNode = callName.getNext();
if (!classNode.isQualifiedName()) {
return null;
}
return callName.getNext().getQualifiedName();
}
@Override
public void applySingletonGetter(
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>FunctionType functionType,
FunctionType getterType, ObjectType objectType) {
functionType.defineDeclaredProperty("getInstance", getterType, false);
functionType.defineDeclaredProperty("instance_", objectType, false);
}
@Override
public String getGlobalObject() {
return "goog.global";
}
private final Set<String> propertyTestFunctions = ImmutableSet.of(
"goog.isDef", "goog.isNull", "goog.isDefAndNotNull",
"goog.isString", "goog.isNumber", "goog.isBoolean",
"goog.isFunction", "goog.isArray", "goog.isObject");
@Override
public boolean isPropertyTestFunction(Node call) {
Preconditions.checkArgument(call.getType() == Token.CALL);
return propertyTestFunctions.contains(
call.getFirstChild().getQualifiedName());
}
@Override
public ObjectLiteralCast getObjectLiteralCast(NodeTraversal t,
Node callNode) {
Preconditions.checkArgument(callNode.getType() == Token.CALL);
Node callName = callNode.getFirstChild();
if (!"goog.reflect.object".equals(callName.getQualifiedName()) ||
callName.getChildCount() != 2) {
return null;
}
Node typeNode = callName.getNext();
if (!typeNode.isQualifiedName()) {
return null;
}
Node objectNode = typeNode.getNext();
if (objectNode.getType() != Token.OBJECTLIT) {
t.getCompiler().report(JSError.make(t.getSourceName(), callNode,
OBJECTLIT_EXPECTED));
return null;
}
return new ObjectLiteralCast(typeNode.getQualifiedName(),
typeNode.getNext());
}
/**
* {@inheritDoc}
*/
@Override
public boolean isOptionalParameter(Node parameter) {
return false;
}
/**
* {@inheritDoc}
*/
@Override
public boolean isVarArgsParameter(Node parameter) {
return false;
}
/**
* {@inheritDoc}
*/
@Override
public boolean isPrivate(String name) {
return false;
}
}
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> int charno) {
this(nodeType, children);
sourcePosition = mergeLineCharNo(lineno, charno);
}
public Node(int nodeType, Node[] children) {
this.type = nodeType;
parent = null;
if (children.length != 0) {
this.first = children[0];
this.last = children[children.length - 1];
for (int i = 1; i < children.length; i++) {
if (null != children[i - 1].next) {
// fail early on loops. implies same node in array twice
throw new IllegalArgumentException("duplicate child");
}
children[i - 1].next = children[i];
Preconditions.checkArgument(children[i - 1].parent == null);
children[i - 1].parent = this;
}
Preconditions.checkArgument(
children[children.length - 1].parent == null);
children[children.length - 1].parent = this;
if (null != this.last.next) {
// fail early on loops. implies same node in array twice
throw new IllegalArgumentException("duplicate child");
}
}
}
public static Node newNumber(double number) {
return new NumberNode(number);
}
public static Node newNumber(double number, int lineno, int charno) {
return new NumberNode(number, lineno, charno);
}
public static Node newString(String str) {
return new StringNode(Token.STRING, str);
}
public static Node newString(int type, String str) {
return new StringNode(type, str);
}
public static Node newString(String str, int lineno, int charno) {
return new StringNode(Token.STRING, str, lineno, charno);
}
public static Node newString(int type, String str, int lineno, int charno) {
return new StringNode(type, str, lineno, charno);
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public boolean hasChildren() {
return first != null;
}
public Node getFirstChild() {
return first;
}
public Node getLastChild
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>() {
return last;
}
public Node getNext() {
return next;
}
public Node getChildBefore(Node child) {
if (child == first)
return null;
Node n = first;
while (n.next != child) {
n = n.next;
if (n == null)
throw new RuntimeException("node is not a child");
}
return n;
}
public Node getChildAtIndex(int i) {
Node n = first;
while (i > 0) {
n = n.next;
i--;
}
return n;
}
public Node getLastSibling() {
Node n = this;
while (n.next != null) {
n = n.next;
}
return n;
}
public void addChildToFront(Node child) {
Preconditions.checkArgument(child.parent == null);
Preconditions.checkArgument(child.next == null);
child.parent = this;
child.next = first;
first = child;
if (last == null) {
last = child;
}
}
public void addChildToBack(Node child) {
Preconditions.checkArgument(child.parent == null);
Preconditions.checkArgument(child.next == null);
child.parent = this;
child.next = null;
if (last == null) {
first = last = child;
return;
}
last.next = child;
last = child;
}
public void addChildrenToFront(Node children) {
for (Node child = children; child != null; child = child.next) {
Preconditions.checkArgument(child.parent == null);
child.parent = this;
}
Node lastSib = children.getLastSibling();
lastSib.next = first;
first = children;
if (last == null) {
last = lastSib;
}
}
public void addChildrenToBack(Node children) {
for (Node child = children; child != null; child = child.next) {
Preconditions.checkArgument(child.parent == null);
child.parent = this;
}
if (last != null) {
last.next = children;
}
last = children.getLastSibling();
if (first == null)
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>
sb.append(" : ");
sb.append(jsTypeString);
}
}
}
}
}
public String toStringTree() {
return toStringTreeImpl();
}
private String toStringTreeImpl() {
try {
StringBuffer s = new StringBuffer();
appendStringTree(s);
return s.toString();
} catch (IOException e) {
throw new RuntimeException("Should not happen\n" + e);
}
}
public void appendStringTree(Appendable appendable) throws IOException {
toStringTreeHelper(this, 0, appendable);
}
private static void toStringTreeHelper(Node n, int level, Appendable sb)
throws IOException
{
if (Token.printTrees) {
for (int i = 0; i != level; ++i) {
sb.append(" ");
}
sb.append(n.toString());
sb.append('\n');
for (Node cursor = n.getFirstChild(); cursor != null;
cursor = cursor.getNext())
{
toStringTreeHelper(cursor, level + 1, sb);
}
}
}
int type; // type of the node; Token.NAME for example
Node next; // next sibling
private Node first; // first element of a linked list of children
private Node last; // last element of a linked list of children
/**
* Linked list of properties. Since vast majority of nodes would have
* no more then 2 properties, linked list saves memory and provides
* fast lookup. If this does not holds, propListHead can be replaced
* by UintMap.
*/
private PropListItem propListHead;
/**
* COLUMN_BITS represents how many of the lower-order bits of
* sourcePosition are reserved for storing the column number.
* Bits above these store the line number.
* This gives us decent position information for everything except
* files already passed through a minimizer, where lines might
* be longer than 4096 characters.
*/
public static final int COLUMN_BITS = 12;
/**
* MAX_COLUMN_NUMBER represents the maximum column number that can
* be represented. JSCompiler's modifications to Rhino cause all
* tokens located beyond the maximum column to MAX_COLUMN_NUMBER.
*/
public static final int MAX
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> unlikely.
return (new SiblingNodeIterable(start)).iterator();
}
}
public boolean hasNext() {
return current != null;
}
public Node next() {
if (current == null) {
throw new NoSuchElementException();
}
try {
return current;
} finally {
current = current.getNext();
}
}
public void remove() {
throw new UnsupportedOperationException();
}
}
//==========================================================================
// Accessors
public Node getParent() {
return parent;
}
/**
* Gets the ancestor node relative to this.
* @param level 0 = this, 1 = the parent, etc.
*/
public Node getAncestor(int level) {
Preconditions.checkArgument(level >= 0);
Node node = this;
while(node != null && level-- > 0) {
node = node.getParent();
}
return node;
}
/**
* Iterates all of the node's ancestors excluding itself.
*/
public AncestorIterable getAncestors() {
return new AncestorIterable(this.getParent());
}
/**
* Iterator to go up the ancestor tree.
*/
public static class AncestorIterable implements Iterable<Node> {
private Node cur;
/**
* @param cur The node to start.
*/
AncestorIterable(Node cur) {
this.cur = cur;
}
public Iterator<Node> iterator() {
return new Iterator<Node>() {
public boolean hasNext() {
return cur != null;
}
public Node next() {
if (!hasNext()) throw new NoSuchElementException();
Node n = cur;
cur = cur.getParent();
return n;
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
}
/**
* Check for one child more efficiently than by iterating over all the
* children as is done with Node.getChildCount().
* @return Whether the node has exactly one child.
*/
public boolean hasOneChild() {
return first != null && first == last;
}
/**
* Check for more than one child more efficiently than by iterating over all
* the children as is done with Node.getChildCount().
* @return Whether the node more than one child.
*/
public boolean hasMoreThanOneChild()
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> {
return first != null && first != last;
}
public int getChildCount() {
int c = 0;
for (Node n = first; n != null; n = n.next)
c++;
return c;
}
// Intended for testing and verification only.
public boolean hasChild(Node child) {
for (Node n = first; n != null; n = n.getNext()) {
if (child == n) {
return true;
}
}
return false;
}
/**
* Checks if the subtree under this node is the same as another subtree.
* Returns null if it's equal, or a message describing the differences.
*/
public String checkTreeEquals(Node node2) {
NodeMismatch diff = checkTreeEqualsImpl(node2);
if (diff != null) {
return "Node tree inequality:" +
"\nTree1:\n" + toStringTree() +
"\n\nTree2:\n" + node2.toStringTree() +
"\n\nSubtree1: " + diff.nodeA.toStringTree() +
"\n\nSubtree2: " + diff.nodeB.toStringTree();
}
return null;
}
/**
* If this is a compilation pass and not a test, do not construct error
* strings. Instead return true if the trees are equal.
*/
public boolean checkTreeEqualsSilent(Node node2) {
return checkTreeEqualsImpl(node2) == null;
}
/**
* Compare this node to node2 recursively and return the first pair
* of nodes that differs doing a preorder depth-first traversal.
* Package private for testing. Returns null if the nodes are equivalent.
*/
NodeMismatch checkTreeEqualsImpl(Node node2) {
boolean eq = false;
if (type == node2.getType() &&
getChildCount() == node2.getChildCount() &&
getClass() == node2.getClass()) {
eq = this.isEquivalentTo(node2);
}
if (!eq) {
return new NodeMismatch(this, node2);
}
NodeMismatch res = null;
Node n, n2;
for (n = first, n2 = node2.first;
res == null && n != null;
n
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> = n.next, n2 = n2.next) {
res = n.checkTreeEqualsImpl(n2);
if (res != null) {
return res;
}
}
return res;
}
/**
* Checks if the subtree under this node is the same as another subtree
* including types. Returns null if it's equal, or a message describing the
* differences.
*/
public boolean checkTreeTypeAwareEqualsSilent(Node node2) {
return checkTreeTypeAwareEqualsImpl(node2) == null;
}
/**
* Compare this node to node2 recursively and return the first pair
* of nodes that differs doing a preorder depth-first traversal.
* Package private for testing. Returns null if the nodes are equivalent.
*/
NodeMismatch checkTreeTypeAwareEqualsImpl(Node node2) {
boolean eq = false;
if (type == node2.getType() &&
getChildCount() == node2.getChildCount() &&
getClass() == node2.getClass() &&
Objects.equal(jsType, node2.getJSType())) {
eq = this.isEquivalentTo(node2);
}
if (!eq) {
return new NodeMismatch(this, node2);
}
NodeMismatch res = null;
Node n, n2;
for (n = first, n2 = node2.first;
res == null && n != null;
n = n.next, n2 = n2.next) {
res = n.checkTreeTypeAwareEqualsImpl(n2);
if (res != null) {
return res;
}
}
return res;
}
public static String tokenToName(int token) {
switch (token) {
case Token.ERROR: return "error";
case Token.EOF: return "eof";
case Token.EOL: return "eol";
case Token.ENTERWITH: return "enterwith";
case Token.LEAVEWITH: return "leavewith";
case Token.RETURN: return "return";
case Token.GOTO: return "goto";
case Token.IFEQ: return "ifeq";
case Token.IFNE: return "ifne";
case Token.SETNAME: return "setname";
case Token.BIT
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>.INC:
case Token.DEC:
case Token.EXPORT:
case Token.IMPORT:
case Token.IF:
case Token.ELSE:
case Token.SWITCH:
case Token.WHILE:
case Token.DO:
case Token.FOR:
case Token.BREAK:
case Token.CONTINUE:
case Token.VAR:
case Token.CONST:
case Token.WITH:
case Token.CATCH:
case Token.FINALLY:
case Token.BLOCK:
case Token.LABEL:
case Token.TARGET:
case Token.LOOP:
case Token.JSR:
case Token.SETPROP_OP:
case Token.SETELEM_OP:
case Token.LOCAL_BLOCK:
case Token.SET_REF_OP:
return true;
default:
return false;
}
}
/**
* This function takes a set of GETPROP nodes and produces a string that is
* each property separated by dots. If the node ultimately under the left
* sub-tree is not a simple name, this is not a valid qualified name.
*
* @return a null if this is not a qualified name, or a dot-separated string
* of the name and properties.
*/
public String getQualifiedName() {
if (type == Token.NAME) {
return getString();
} else if (type == Token.GETPROP) {
String left = getFirstChild().getQualifiedName();
if (left == null) {
return null;
}
return left + "." + getLastChild().getString();
} else if (type == Token.THIS) {
return "this";
} else {
return null;
}
}
/**
* Returns whether a node corresponds to a simple or a qualified name, such
* as <code>x</code> or <code>a.b.c</code> or <code>this.a</code>.
*/
public boolean isQualifiedName() {
switch (getType()) {
case Token.NAME:
case Token.THIS:
return true;
case Token.GETPROP:
return getFirstChild().isQualifiedName();
default:
return false;
}
}
/**
* Returns whether a node corresponds to a simple or a qualified name
* without a "this" reference, such as <code>a.
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>b.c</code>, but not
* <code>this.a</code>.
*/
public boolean isUnscopedQualifiedName() {
switch (getType()) {
case Token.NAME:
return true;
case Token.GETPROP:
return getFirstChild().isUnscopedQualifiedName();
default:
return false;
}
}
//==========================================================================
// Mutators
/**
* Removes this node from its parent. Equivalent to:
* node.getParent().removeChild();
*/
public Node detachFromParent() {
Preconditions.checkState(parent != null);
parent.removeChild(this);
return this;
}
/**
* Removes the first child of Node. Equivalent to:
* node.removeChild(node.getFirstChild());
* @return The removed Node.
*/
public Node removeFirstChild() {
Node child = first;
if (child != null) {
removeChild(child);
}
return child;
}
/**
* @return A Node that is the head of the list of children.
*/
public Node removeChildren() {
Node children = first;
for (Node child = first; child != null; child = child.getNext()) {
child.parent = null;
}
first = null;
last = null;
return children;
}
/**
* Removes all children from this node and isolates the children from each
* other.
*/
public void detachChildren() {
for (Node child = first; child != null; ) {
Node nextChild = child.getNext();
child.parent = null;
child.next = null;
child = nextChild;
}
first = null;
last = null;
}
public Node removeChildAfter(Node prev) {
Preconditions.checkArgument(prev.parent == this,
"prev is not a child of this node.");
Preconditions.checkArgument(prev.next != null,
"no next sibling.");
Node child = prev.next;
prev.next = child.next;
if (child == last) last = prev;
child.next = null;
child.parent = null;
return child;
}
/**
* @return A detached clone of the Node, specifically excluding its
* children.
*/
public Node cloneNode()
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> {
Node result;
try {
result = (Node) super.clone();
result.next = null;
result.first = null;
result.last = null;
result.parent = null;
}
catch (CloneNotSupportedException e) {
throw new RuntimeException(e.getMessage());
}
return result;
}
/**
* @return A detached clone of the Node and all its children.
*/
public Node cloneTree() {
Node result = cloneNode();
for (Node n2 = getFirstChild(); n2 != null; n2 = n2.getNext()) {
Node n2clone = n2.cloneTree();
n2clone.parent = result;
if (result.last != null) {
result.last.next = n2clone;
}
if (result.first == null) {
result.first = n2clone;
}
result.last = n2clone;
}
return result;
}
/**
* Copies source file and name information from the other
* node given to the current node. Used for maintaining
* debug information across node append and remove operations.
* @return this
*/
public Node copyInformationFrom(Node other) {
if (getProp(ORIGINALNAME_PROP) == null) {
putProp(ORIGINALNAME_PROP, other.getProp(ORIGINALNAME_PROP));
}
if (getProp(SOURCEFILE_PROP) == null) {
putProp(SOURCEFILE_PROP, other.getProp(SOURCEFILE_PROP));
sourcePosition = other.sourcePosition;
}
return this;
}
/**
* Copies source file and name information from the other node to the
* entire tree rooted at this node.
* @return this
*/
public Node copyInformationFromForTree(Node other) {
copyInformationFrom(other);
for (Node child = getFirstChild();
child != null; child = child.getNext()) {
child.copyInformationFromForTree(other);
}
return this;
}
//==========================================================================
// Custom annotations
public JSType getJSType() {
return jsType;
}
public void setJSType(JSType jsType) {
this.jsType = jsType;
}
public FileLevelJsDocBuilder
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> null) {
switch (parent.getType()) {
case Token.FOR:
// Only traverse the body of the for loop.
return n == parent.getLastChild();
// Skip the conditions.
case Token.IF:
case Token.WHILE:
case Token.WITH:
return n != parent.getFirstChild();
case Token.DO:
return n != parent.getFirstChild().getNext();
// Only traverse the body of the cases
case Token.SWITCH:
case Token.CASE:
case Token.CATCH:
case Token.LABEL:
return n != parent.getFirstChild();
case Token.FUNCTION:
return n == parent.getFirstChild().getNext().getNext();
case Token.CONTINUE:
case Token.BREAK:
case Token.EXPR_RESULT:
case Token.VAR:
case Token.RETURN:
case Token.THROW:
return false;
case Token.TRY:
/* Just before we are about to visit the second child of the TRY node,
* we know that we will be visiting either the CATCH or the FINALLY.
* In other words, we know that the post order traversal of the TRY
* block has been finished, no more exceptions can be caught by the
* handler at this TRY block and should be taken out of the stack.
*/
if (n == parent.getFirstChild().getNext()) {
Preconditions.checkState(exceptionHandler.peek() == parent);
exceptionHandler.pop();
}
}
}
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.IF:
handleIf(n);
return;
case Token.WHILE:
handleWhile(n);
return;
case Token.DO:
handleDo(n);
return;
case Token.FOR:
handleFor(n);
return;
case Token.SWITCH:
handleSwitch(n);
return;
case Token.CASE:
handleCase(n);
return;
case Token.DEFAULT:
handleDefault(n);
return;
case Token.BLOCK:
case Token.SCRIPT:
handleStmtList(n);
return;
case Token.FUNCTION:
handleFunction(n);
return;
case Token.EXPR_RESULT:
handle
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>Expr(n);
return;
case Token.THROW:
handleThrow(n);
return;
case Token.TRY:
handleTry(n);
return;
case Token.CATCH:
handleCatch(n);
return;
case Token.BREAK:
handleBreak(n);
return;
case Token.CONTINUE:
handleContinue(n);
return;
case Token.RETURN:
handleReturn(n);
return;
case Token.WITH:
handleWith(n);
return;
case Token.LABEL:
return;
default:
handleStmt(n);
return;
}
}
private void handleIf(Node node) {
Node thenBlock = node.getFirstChild().getNext();
Node elseBlock = thenBlock.getNext();
createEdge(node, Branch.ON_TRUE, computeFallThrough(thenBlock));
if (elseBlock == null) {
createEdge(node, Branch.ON_FALSE,
computeFollowNode(node)); // not taken branch
} else {
createEdge(node, Branch.ON_FALSE, computeFallThrough(elseBlock));
}
connectToPossibleExceptionHandler(
node, NodeUtil.getConditionExpression(node));
}
private void handleWhile(Node node) {
// Control goes to the first statement if the condition evaluates to true.
createEdge(node, Branch.ON_TRUE,
computeFallThrough(node.getFirstChild().getNext()));
// Control goes to the follow() if the condition evaluates to false.
createEdge(node, Branch.ON_FALSE,
computeFollowNode(node));
connectToPossibleExceptionHandler(
node, NodeUtil.getConditionExpression(node));
}
private void handleDo(Node node) {
// The first edge can be the initial iteration as well as the iterations
// after.
createEdge(node, Branch.ON_TRUE, computeFallThrough(node.getFirstChild()));
// The edge that leaves the do loop if the condition fails.
createEdge(node, Branch.ON_FALSE,
computeFollowNode(node));
connectToPossibleExceptionHandler(
node, NodeUtil.getConditionExpression(node));
}
private void handleFor(Node forNode) {
if (forNode.getChildCount() == 4) {
// We have for (init; cond; iter) {
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> body }
Node init = forNode.getFirstChild();
Node cond = init.getNext();
Node iter = cond.getNext();
Node body = iter.getNext();
// After initialization, we transfer to the FOR which is in charge of
// checking the condition (for the first time).
createEdge(init, Branch.UNCOND, forNode);
// The edge that transfer control to the beginning of the loop body.
createEdge(forNode, Branch.ON_TRUE, computeFallThrough(body));
// The edge to end of the loop.
createEdge(forNode, Branch.ON_FALSE,
computeFollowNode(forNode));
// The end of the body will have a unconditional branch to our iter
// (handled by calling computeFollowNode of the last instruction of the
// body. Our iter will jump to the forNode again to another condition
// check.
createEdge(iter, Branch.UNCOND, forNode);
connectToPossibleExceptionHandler(init, init);
connectToPossibleExceptionHandler(forNode, cond);
connectToPossibleExceptionHandler(iter, iter);
} else {
// We have for (item in collection) { body }
Node item = forNode.getFirstChild();
Node collection = item.getNext();
Node body = collection.getNext();
// The edge that transfer control to the beginning of the loop body.
createEdge(forNode, Branch.ON_TRUE, computeFallThrough(body));
// The edge to end of the loop.
createEdge(forNode, Branch.ON_FALSE,
computeFollowNode(forNode));
connectToPossibleExceptionHandler(forNode, collection);
}
}
private void handleSwitch(Node node) {
// Transfer to the first non-DEFAULT CASE. if there are none, transfer
// to the DEFAULT or the EMPTY node.
Node next = getNextSiblingOfType(
node.getFirstChild().getNext(), Token.CASE, Token.EMPTY);
if (next != null) { // Has at least one CASE or EMPTY
createEdge(node, Branch.UNCOND, next);
} else { // Has no CASE but possibly a DEFAULT
if (node.getFirstChild().getNext() != null) {
createEdge(node, Branch.UNCOND, node.getFirstChild().getNext());
} else { // No CASE, no DEFAULT
createEdge(node
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>, Branch.UNCOND, computeFollowNode(node));
}
}
connectToPossibleExceptionHandler(node, node.getFirstChild());
}
private void handleCase(Node node) {
// Case is a bit tricky....First it goes into the body if condition is true.
createEdge(node, Branch.ON_TRUE,
node.getFirstChild().getNext());
// Look for the next CASE, skipping over DEFAULT.
Node next = getNextSiblingOfType(node.getNext(), Token.CASE);
if (next != null) { // Found a CASE
Preconditions.checkState(next.getType() == Token.CASE);
createEdge(node, Branch.ON_FALSE, next);
} else { // No more CASE found, go back and search for a DEFAULT.
Node parent = node.getParent();
Node deflt = getNextSiblingOfType(
parent.getFirstChild().getNext(), Token.DEFAULT);
if (deflt != null) { // Has a DEFAULT
createEdge(node, Branch.ON_FALSE, deflt);
} else { // No DEFAULT found, go to the follow of the SWITCH.
createEdge(node, Branch.ON_FALSE, computeFollowNode(node));
}
}
connectToPossibleExceptionHandler(node, node.getFirstChild());
}
private void handleDefault(Node node) {
// Directly goes to the body. It should not transfer to the next case.
createEdge(node, Branch.UNCOND, node.getFirstChild());
}
private void handleWith(Node node) {
// Directly goes to the body. It should not transfer to the next case.
createEdge(node, Branch.UNCOND, node.getLastChild());
connectToPossibleExceptionHandler(node, node.getFirstChild());
}
private void handleStmtList(Node node) {
Node parent = node.getParent();
// Special case, don't add a block of empty CATCH block to the graph.
if (node.getType() == Token.BLOCK && parent != null &&
parent.getType() == Token.TRY &&
NodeUtil.getCatchBlock(parent) == node &&
!NodeUtil.hasCatchHandler(node)) {
return;
}
// A block transfer control to its first child if it is not empty.
Node child = node.getFirstChild();
// Function declarations are skipped since
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> control doesn't go into that
// function (unless it is called)
while (child != null && child.getType() == Token.FUNCTION) {
child = child.getNext();
}
if (child != null) {
createEdge(node, Branch.UNCOND, computeFallThrough(child));
} else {
createEdge(node, Branch.UNCOND, computeFollowNode(node));
}
// Synthetic blocks
if (parent != null) {
switch (parent.getType()) {
case Token.DEFAULT:
case Token.CASE:
case Token.TRY:
break;
default:
if (node.getType() == Token.BLOCK && node.isSyntheticBlock()) {
Node next = node.getLastChild();
if (next != null) {
createEdge(node, Branch.SYN_BLOCK, computeFallThrough(next));
}
}
break;
}
}
}
private void handleFunction(Node node) {
// A block transfer control to its first child if it is not empty.
Preconditions.checkState(node.getChildCount() >= 3);
createEdge(node, Branch.UNCOND,
computeFallThrough(node.getFirstChild().getNext().getNext()));
Preconditions.checkState(exceptionHandler.peek() == node);
exceptionHandler.pop();
}
private void handleExpr(Node node) {
createEdge(node, Branch.UNCOND, computeFollowNode(node));
connectToPossibleExceptionHandler(node, node);
}
private void handleThrow(Node node) {
connectToPossibleExceptionHandler(node, node);
}
private void handleTry(Node node) {
createEdge(node, Branch.UNCOND, node.getFirstChild());
}
private void handleCatch(Node node) {
createEdge(node, Branch.UNCOND, node.getLastChild());
}
private void handleBreak(Node node) {
String label = null;
// See if it is a break with label.
if (node.hasChildren()) {
label = node.getFirstChild().getString();
}
Node cur;
Node lastJump;
Node parent = node.getParent();
/*
* Continuously look up the ancestor tree for the BREAK target or the target
* with the corresponding label and connect to it. If along the path we
*
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> discover a FINALLY, we will connect the BREAK to that FINALLY. From then
* on, we will just record the control flow changes in the finallyMap. This
* is due to the fact that we need to connect any node that leaves its own
* FINALLY block to the outer FINALLY or the BREAK's target but those nodes
* are not known yet due to the way we traverse the nodes.
*/
for (cur = node, lastJump = node;
!isBreakTarget(cur, parent, label);
cur = parent, parent = parent.getParent()) {
if (cur.getType() == Token.TRY && NodeUtil.hasFinally(cur)) {
if (lastJump == node) {
createEdge(lastJump, Branch.UNCOND, computeFallThrough(
cur.getLastChild()));
} else {
finallyMap.put(lastJump, computeFallThrough(cur.getLastChild()));
}
lastJump = cur;
}
Preconditions.checkState(parent != null, "Cannot find break target.");
}
if (lastJump == node) {
createEdge(lastJump, Branch.UNCOND, computeFollowNode(cur));
} else {
finallyMap.put(lastJump, computeFollowNode(cur));
}
}
private void handleContinue(Node node) {
String label = null;
if (node.hasChildren()) {
label = node.getFirstChild().getString();
}
Node cur;
Node lastJump;
// Similar to handBreak's logic with a few minor variation.
Node parent = node.getParent();
for (cur = node, lastJump = node;
!isContinueTarget(cur, parent, label);
cur = parent, parent = parent.getParent()) {
if (cur.getType() == Token.TRY && NodeUtil.hasFinally(cur)) {
if (lastJump == node) {
createEdge(lastJump, Branch.UNCOND, cur.getLastChild());
} else {
finallyMap.put(lastJump, computeFallThrough(cur.getLastChild()));
}
lastJump = cur;
}
Preconditions.checkState(parent != null, "Cannot find continue target.");
}
Node iter = cur;
if (cur.getChildCount() == 4) {
iter = cur.getFirstChild().getNext().getNext
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>();
}
if (lastJump == node) {
createEdge(node, Branch.UNCOND, iter);
} else {
finallyMap.put(lastJump, iter);
}
}
private void handleReturn(Node node) {
Node lastJump = null;
for (Iterator<Node> iter = exceptionHandler.iterator(); iter.hasNext();) {
Node curHandler = iter.next();
if (NodeUtil.isFunction(curHandler)) {
break;
}
if (NodeUtil.hasFinally(curHandler)) {
if (lastJump == null) {
createEdge(node, Branch.UNCOND, curHandler.getLastChild());
} else {
finallyMap.put(lastJump,
computeFallThrough(curHandler.getLastChild()));
}
lastJump = curHandler;
}
}
if (node.hasChildren()) {
connectToPossibleExceptionHandler(node, node.getFirstChild());
}
if (lastJump == null) {
createEdge(node, Branch.UNCOND, null);
} else {
finallyMap.put(lastJump, null);
}
}
private void handleStmt(Node node) {
// Simply transfer to the next line.
createEdge(node, Branch.UNCOND, computeFollowNode(node));
connectToPossibleExceptionHandler(node, node);
}
private Node computeFollowNode(Node node) {
return computeFollowNode(node, node);
}
/**
* Computes the follow() node of a given node and its parent. There is a side
* effect when calling this function. If this function computed an edge that
* exists a FINALLY, it'll attempt to connect the fromNode to the outer
* FINALLY according to the finallyMap.
*
* @param fromNode The original source node since {@code node} is changed
* during recursion.
* @param node The node that follow() should compute.
*/
private Node computeFollowNode(Node fromNode, Node node) {
/*
* This is the case where:
*
* 1. Parent is null implies that we are transferring control to the end of
* the script.
*
* 2. Parent is a function implies that we are transferring control back to
* the caller of the function.
*
*
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>3. If the node is a return statement, we should also transfer control
* back to the caller of the function.
*
* 4. If the node is root then we have reached the end of what we have been
* asked to traverse.
*
* In all cases we should transfer control to a "symbolic return" node.
* This will make life easier for DFAs.
*/
Node parent = node.getParent();
if (parent == null || parent.getType() == Token.FUNCTION || node == root) {
return null;
}
// If we are just before a IF/WHILE/DO/FOR:
switch (parent.getType()) {
// The follow() of any of the path from IF would be what follows IF.
case Token.IF:
return computeFollowNode(fromNode, parent);
case Token.CASE:
case Token.DEFAULT:
// After the body of a CASE, the control goes to the body of the next
// case, without having to go to the case condition.
if (parent.getNext() != null) {
if (parent.getNext().getType() == Token.CASE) {
return parent.getNext().getFirstChild().getNext();
} else if (parent.getNext().getType() == Token.DEFAULT) {
return parent.getNext().getFirstChild();
} else {
Preconditions.checkState(false, "Not reachable");
}
} else {
return computeFollowNode(fromNode, parent);
}
break;
case Token.FOR:
if (NodeUtil.isForIn(parent)) {
return parent;
} else {
return parent.getFirstChild().getNext().getNext();
}
case Token.WHILE:
case Token.DO:
return parent;
case Token.TRY:
// If we are coming out of the TRY block...
if (parent.getFirstChild() == node) {
if (NodeUtil.hasFinally(parent)) { // and have FINALLY block.
return computeFallThrough(parent.getLastChild());
} else { // and have no FINALLY.
return computeFollowNode(fromNode, parent);
}
// CATCH block.
} else if (NodeUtil.getCatchBlock(parent) == node){
if (NodeUtil.hasFinally(parent)) { // and have FINALLY block.
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>
return computeFallThrough(node.getNext());
} else {
return computeFollowNode(fromNode, parent);
}
// If we are coming out of the FINALLY block...
} else if (parent.getLastChild() == node){
for (Node finallyNode : finallyMap.get(parent)) {
createEdge(fromNode, Branch.UNCOND, finallyNode);
}
return computeFollowNode(fromNode, parent);
}
}
// Now that we are done with the special cases follow should be its
// immediate sibling, unless its sibling is a function
Node nextSibling = node.getNext();
// Skip function declarations because control doesn't get pass into it.
while (nextSibling != null && nextSibling.getType() == Token.FUNCTION) {
nextSibling = nextSibling.getNext();
}
if (nextSibling != null) {
return computeFallThrough(nextSibling);
} else {
// If there are no more siblings, control is transfered up the AST.
return computeFollowNode(fromNode, parent);
}
}
/**
* Computes the destination node of n when we want to fallthough into the
* subtree of n. We don't always create a CFG edge into n itself because of
* DOs and FORs.
*/
private static Node computeFallThrough(Node n) {
switch (n.getType()) {
case Token.DO:
return computeFallThrough(n.getFirstChild());
case Token.FOR:
if (NodeUtil.isForIn(n)) {
return n;
}
return computeFallThrough(n.getFirstChild());
case Token.LABEL:
return computeFallThrough(n.getLastChild());
default:
return n;
}
}
/**
* Connects the two nodes in the control flow graph.
*
* @param fromNode Source.
* @param toNode Destination.
*/
private void createEdge(Node fromNode, ControlFlowGraph.Branch branch,
Node toNode) {
cfg.createNode(fromNode);
cfg.createNode(toNode);
cfg.connectIfNotFound(fromNode, branch, toNode);
}
/**
* Connects cfgNode to the proper CATCH block if target subtree might throw
* an exception. If there are FINALLY blocks reached before a CATCH, it will
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>
* make the corresponding entry in finallyMap.
*/
private void connectToPossibleExceptionHandler(Node cfgNode, Node target) {
if (mayThrowException(target) && !exceptionHandler.isEmpty()) {
Node lastJump = cfgNode;
for (Node handler : exceptionHandler) {
if (NodeUtil.isFunction(handler)) {
return;
}
Preconditions.checkState(handler.getType() == Token.TRY);
Node catchBlock = NodeUtil.getCatchBlock(handler);
if (!NodeUtil.hasCatchHandler(catchBlock)) { // No catch but a FINALLY.
if (lastJump == cfgNode) {
createEdge(cfgNode, Branch.ON_EX, handler.getLastChild());
} else {
finallyMap.put(lastJump, handler.getLastChild());
}
} else { // Has a catch.
if (lastJump == cfgNode) {
createEdge(cfgNode, Branch.ON_EX, catchBlock);
return;
} else {
finallyMap.put(lastJump, catchBlock);
}
}
lastJump = handler;
}
}
}
/**
* Get the next sibling (including itself) of one of the given types.
*/
private static Node getNextSiblingOfType(Node first, int ... types) {
for (Node c = first; c != null; c = c.getNext()) {
for (int type : types) {
if (c.getType() == type) {
return c;
}
}
}
return null;
}
/**
* Checks if target is actually the break target of labeled continue. The
* label can be null if it is an unlabeled break.
*/
private static boolean isBreakTarget(
Node target, Node parent, String label) {
return isBreakStructure(target, label != null) && matchLabel(parent, label);
}
/**
* Checks if target is actually the continue target of labeled continue. The
* label can be null if it is an unlabeled continue.
*/
private static boolean isContinueTarget(
Node target, Node parent, String label) {
return isContinueStructure(target) && matchLabel(parent, label);
}
/**
* Check if label is actually referencing the target control structure. If
* label is null, it always returns
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> true.
*/
private static boolean matchLabel(Node target, String label) {
if (label == null) {
return true;
}
while (target.getType() == Token.LABEL) {
if (target.getFirstChild().getString().equals(label)) {
return true;
}
target = target.getParent();
}
return false;
}
/**
* Determines if the subtree might throw an exception.
*/
private static boolean mayThrowException(Node n) {
switch (n.getType()) {
case Token.CALL:
case Token.GETPROP:
case Token.GETELEM:
case Token.THROW:
case Token.NEW:
case Token.ASSIGN:
case Token.INC:
case Token.DEC:
case Token.INSTANCEOF:
return true;
case Token.FUNCTION:
return false;
}
for (Node c = n.getFirstChild(); c != null; c = c.getNext()) {
if (!ControlFlowGraph.isEnteringNewCfgNode(c) && mayThrowException(c)) {
return true;
}
}
return false;
}
/**
* Determines whether the given node can be terminated with a BREAK node.
*/
static boolean isBreakStructure(Node n, boolean labeled) {
switch (n.getType()) {
case Token.FOR:
case Token.DO:
case Token.WHILE:
case Token.SWITCH:
return true;
case Token.BLOCK:
case Token.IF:
case Token.TRY:
return labeled;
default:
return false;
}
}
/**
* Determines whether the given node can be advanced with a CONTINUE node.
*/
static boolean isContinueStructure(Node n) {
switch (n.getType()) {
case Token.FOR:
case Token.DO:
case Token.WHILE:
return true;
default:
return false;
}
}
/**
* A {@link ControlFlowGraph} which provides a node comparator based on the
* pre-order traversal of the AST.
*/
private static class AstControlFlowGraph extends ControlFlowGraph<Node> {
private final Map<DiGraphNode<Node, Branch>, Integer> priorities;
/**
* Constructor.
* @param entry The entry node.
* @param
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> return new FunctionType(this, name, source, parameters, returnType, null,
null, true, false);
}
/**
* Creates an interface function type.
* @param name the function's name
* @param source the node defining this function. Its type
* ({@link Node#getType()}) must be {@link Token#FUNCTION}.
*/
public FunctionType createInterfaceType(String name, Node source) {
return new FunctionType(this, name, source);
}
/**
* Creates a parameterized type.
*/
public ParameterizedType createParameterizedType(
ObjectType objectType, JSType parameterType) {
return new ParameterizedType(this, objectType, parameterType);
}
/**
* Identifies the name of an enum before we actually declare it.
*/
public void identifyEnumName(String name) {
enumTypeNames.add(name);
}
/**
* Creates a RecordType from the nodes representing said record type.
* @param n The node with type info.
* @param sourceName The source file name.
* @param scope A scope for doing type name lookups.
*/
public JSType createRecordTypeFromNodes(Node n, String sourceName,
StaticScope<JSType> scope) {
RecordTypeBuilder builder = new RecordTypeBuilder(this);
// For each of the fields in the record type.
for (Node fieldTypeNode = n.getFirstChild();
fieldTypeNode != null;
fieldTypeNode = fieldTypeNode.getNext()) {
// Get the property's name.
Node fieldNameNode = fieldTypeNode;
boolean hasType = false;
if (fieldTypeNode.getType() == Token.COLON) {
fieldNameNode = fieldTypeNode.getFirstChild();
hasType = true;
}
String fieldName = fieldNameNode.getString();
// TODO(user): Move this into the lexer/parser.
// Remove the string literal characters around a field name,
// if any.
if (fieldName.startsWith("'") || fieldName.startsWith("\"")) {
fieldName = fieldName.substring(1, fieldName.length() - 1);
}
// Get the property's type.
JSType fieldType = null;
if (hasType) {
// We have a declared type.
fieldType = createFromTypeNodes(
fieldTypeNode.getLastChild(), source
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>Name, scope);
} else {
// Otherwise, the type is UNKNOWN.
fieldType = getNativeType(JSTypeNative.UNKNOWN_TYPE);
}
// Add the property to the record.
builder.addProperty(fieldName, fieldType);
}
return builder.build();
}
/**
* Creates a JSType from the nodes representing a type.
* @param n The node with type info.
* @param sourceName The source file name.
* @param scope A scope for doing type name lookups.
*/
public JSType createFromTypeNodes(Node n, String sourceName,
StaticScope<JSType> scope) {
switch (n.getType()) {
case Token.LC: // Record type.
return createRecordTypeFromNodes(n.getFirstChild(), sourceName, scope);
case Token.BANG: // Not nullable
return createFromTypeNodes(n.getFirstChild(), sourceName, scope)
.restrictByNotNullOrUndefined();
case Token.QMARK: // Nullable
return createNullableType(
createFromTypeNodes(n.getFirstChild(), sourceName, scope));
case Token.EQUALS: // Optional
return createOptionalType(
createFromTypeNodes(n.getFirstChild(), sourceName, scope));
case Token.ELLIPSIS: // Var args
return createOptionalType(
createFromTypeNodes(n.getFirstChild(), sourceName, scope));
case Token.STAR: // The AllType
return getNativeType(ALL_TYPE);
case Token.LB: // Array type
// TODO(nicksantos): Enforce membership restrictions on the Array.
return getNativeType(ARRAY_TYPE);
case Token.PIPE: // Union type
UnionTypeBuilder builder = new UnionTypeBuilder(this);
for (Node child = n.getFirstChild(); child != null;
child = child.getNext()) {
builder.addAlternate(createFromTypeNodes(child, sourceName, scope));
}
return builder.build();
case Token.EMPTY: // When the return value of a function is not specified
return getNativeType(UNKNOWN_TYPE);
case Token.VOID: // Only allowed in the return value of a function.
return getNativeType(VOID_TYPE);
case Token.STRING:
JSType namedType = getType(scope, n.getString(), source
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>Name,
n.getLineno(), n.getCharno());
if ((namedType instanceof ObjectType) &&
!(enumTypeNames.contains(n.getString()))) {
Node typeList = n.getFirstChild();
if (typeList != null &&
("Array".equals(n.getString()) ||
"Object".equals(n.getString()))) {
JSType parameterType =
createFromTypeNodes(
typeList.getLastChild(), sourceName, scope);
namedType = new ParameterizedType(
this, (ObjectType) namedType, parameterType);
if (typeList.hasMoreThanOneChild()) {
JSType indexType =
createFromTypeNodes(
typeList.getFirstChild(), sourceName, scope);
namedType = new IndexedType(
this, (ObjectType) namedType, indexType);
}
}
return createNullableType(namedType);
} else {
return namedType;
}
case Token.FUNCTION:
ObjectType thisType = null;
Node current = n.getFirstChild();
if (current.getType() == Token.THIS) {
Node thisNode = current.getFirstChild();
thisType =
ObjectType.cast(
createFromTypeNodes(thisNode, sourceName, scope)
.restrictByNotNullOrUndefined());
if (thisType == null) {
reporter.warning(
ScriptRuntime.getMessage0("msg.jsdoc.function.thisnotobject"),
sourceName, thisNode.getLineno(), "", thisNode.getCharno());
}
current = current.getNext();
}
FunctionParamBuilder paramBuilder = new FunctionParamBuilder(this);
if (current.getType() == Token.LP) {
Node args = current.getFirstChild();
for (Node arg = current.getFirstChild(); arg != null;
arg = arg.getNext()) {
if (arg.getType() == Token.ELLIPSIS) {
if (arg.getChildCount() == 0) {
paramBuilder.addVarArgs(getNativeType(UNKNOWN_TYPE));
} else {
paramBuilder.addVarArgs(
createFromTypeNodes(
arg.getFirstChild(), sourceName, scope));
}
} else {
JSType type = createFromTypeNodes(arg, sourceName, scope);
if (arg.getType() == Token.EQUALS)
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> {
boolean addSuccess = paramBuilder.addOptionalParams(type);
if (!addSuccess) {
reporter.warning(
ScriptRuntime.getMessage0("msg.jsdoc.function.varargs"),
sourceName, arg.getLineno(), "", arg.getCharno());
}
} else {
paramBuilder.addRequiredParams(type);
}
}
}
current = current.getNext();
}
JSType returnType = createFromTypeNodes(current, sourceName, scope);
return new FunctionType(this, null, null, paramBuilder.build(),
returnType, thisType, null);
}
throw new IllegalStateException(
"Unexpected node in type expression: " + n.toString());
}
/**
* Sets the template type name.
*/
public void setTemplateTypeName(String name) {
templateTypeName = name;
templateType = new TemplateType(this, name);
}
/**
* Clears the template type name.
*/
public void clearTemplateTypeName() {
templateTypeName = null;
templateType = null;
}
}
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>EnteringNewCfgNode(n);
}
}
/**
* @return True if n should be represented by a new CFG node in the control
* flow graph.
*/
public static boolean isEnteringNewCfgNode(Node n) {
Node parent = n.getParent();
switch (parent.getType()) {
case Token.BLOCK:
case Token.SCRIPT:
case Token.TRY:
case Token.FINALLY:
return true;
case Token.FUNCTION:
// A function node represents the start of a function where the name
// is bleed into the local scope and parameters has been assigned
// to the formal argument names. The node includes the name of the
// function and the LP list since we assume the whole set up process
// is atomic without change in control flow. The next change of
// control is going into the function's body represent by the second
// child.
return n != parent.getFirstChild().getNext();
case Token.WHILE:
case Token.DO:
case Token.IF:
// Theses control structure is represented by its node that holds the
// condition. Each of them is a branch node based on its condition.
return NodeUtil.getConditionExpression(parent) != n;
case Token.FOR:
// The FOR(;;) node differs from other control structure in that
// it has a initialization and a increment statement. Those
// two statements have its corresponding CFG nodes to represent them.
// The FOR node represents the condition check for each iteration.
// That way the following:
// for(var x = 0; x < 10; x++) { } has a graph that is isomorphic to
// var x = 0; while(x<10) { x++; }
if (NodeUtil.isForIn(parent)) {
return n == parent.getLastChild();
} else {
return NodeUtil.getConditionExpression(parent) != n;
}
case Token.SWITCH:
case Token.CASE:
case Token.CATCH:
case Token.WITH:
return n != parent.getFirstChild();
default:
return false;
}
}
}
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>
*
*/
final class ArrowType extends JSType {
private static final long serialVersionUID = 1L;
final Node parameters;
JSType returnType;
ArrowType(JSTypeRegistry registry, Node parameters,
JSType returnType) {
super(registry);
this.parameters = parameters;
this.returnType = returnType;
}
@Override
public boolean isSubtype(JSType other) {
if (!(other instanceof ArrowType)) {
return false;
}
ArrowType that = (ArrowType) other;
// this.returnType <: that.returnType (covariant)
// If the return type is null, this is equivalent to unknown so we do not
// base our decision on that.
if (this.returnType != null &&
that.returnType != null &&
!this.returnType.isSubtype(that.returnType)) {
return false;
}
// that.paramType[i] <: this.paramType[i] (contravariant)
// TODO(nicksantos): This is incorrect. It should be invariant.
// Follow up with closure team on how to fix this without everyone
// hating on us.
//
// If the parameter list is null, this is equivalent of ?... so we do not
// base our decision on that.
if (this.parameters != null && that.parameters != null) {
Node thisParam = parameters.getFirstChild();
Node thatParam = that.parameters.getFirstChild();
while (thisParam != null && thatParam != null) {
JSType thisParamType = thisParam.getJSType();
if (thisParamType != null) {
JSType thatParamType = thatParam.getJSType();
if (thatParamType == null ||
!thatParamType.isSubtype(thisParamType)) {
return false;
}
}
boolean thisIsVarArgs = thisParam.isVarArgs();
boolean thatIsVarArgs = thatParam.isVarArgs();
// don't advance if we have variable arguments
if (!thisIsVarArgs) {
thisParam = thisParam.getNext();
}
if (!thatIsVarArgs) {
thatParam = thatParam.getNext();
}
// both var_args indicates the end
if (thisIsVarArgs && thatIsVarArgs) {
thisParam = null;
that
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>Param = null;
}
}
// Right now, the parser's type system doesn't have a good way
// to model optional arguments.
//
// Suppose we have
// function f(number, number) {}
// function g(number) {}
// If the second arg of f is optional, then f is a subtype of g,
// but g is not a subtype of f.
// If the second arg of f is required, then g is a subtype of f,
// but f is not a subtype of g.
//
// Until we model optional params, let's just punt on this.
// If one type has more arguments than the other, we won't check them.
//
// NOTE(nicksantos): This is described in Draft 2 of the ES4 spec,
// Section 3.4.6: Subtyping Function Types. It seems really
// strange but I haven't thought a lot about the implementation.
}
return true;
}
@Override
public boolean equals(Object object) {
// Please keep this method in sync with the hashCode() method below.
if (!(object instanceof ArrowType)) {
return false;
}
ArrowType that = (ArrowType) object;
// if both return types are specified, then they should be equal
if (returnType == null) {
if (that.returnType != null) {
return false;
}
} else {
if (that.returnType == null) {
return false;
}
if (!returnType.equals(that.returnType)) {
return false;
}
}
// if both types include parameters, the lists should be the same
if (parameters == null) {
return that.parameters == null;
} else if (that.parameters == null) {
return false;
}
Node thisParam = parameters.getFirstChild();
Node otherParam = that.parameters.getFirstChild();
while (thisParam != null && otherParam != null) {
JSType thisParamType = thisParam.getJSType();
JSType otherParamType = otherParam.getJSType();
if (thisParamType != null) {
// Both parameter lists give a type for this param, it should be equal
if (otherParamType != null &&
!thisParamType.equals(otherParamType)) {
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>
return false;
}
} else {
if (otherParamType != null) {
return false;
}
}
thisParam = thisParam.getNext();
otherParam = otherParam.getNext();
}
// One of the parameters is null, so the types are only equal if both
// parameter lists are null (they are equal).
return thisParam == otherParam;
}
@Override
public int hashCode() {
int hashCode = 0;
if (returnType != null) {
hashCode += returnType.hashCode();
}
if (parameters != null) {
Node param = parameters.getFirstChild();
while (param != null) {
JSType paramType = param.getJSType();
if (paramType != null) {
hashCode += paramType.hashCode();
}
param = param.getNext();
}
}
return hashCode;
}
@Override
public JSType getLeastSupertype(JSType that) {
throw new UnsupportedOperationException();
}
@Override
public JSType getGreatestSubtype(JSType that) {
throw new UnsupportedOperationException();
}
@Override
public TernaryValue testForEquality(JSType that) {
throw new UnsupportedOperationException();
}
@Override
public <T> T visit(Visitor<T> visitor) {
throw new UnsupportedOperationException();
}
@Override
public BooleanLiteralSet getPossibleToBooleanOutcomes() {
return BooleanLiteralSet.TRUE;
}
@Override
JSType resolveInternal(ErrorReporter t, StaticScope<JSType> scope) {
returnType = safeResolve(returnType, t, scope);
if (parameters != null) {
for (Node paramNode = parameters.getFirstChild();
paramNode != null; paramNode = paramNode.getNext()) {
paramNode.setJSType(paramNode.getJSType().resolve(t, scope));
}
}
return this;
}
}
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS>.kind = Kind.INTERFACE;
this.typeOfThis = new InstanceObjectType(registry, this);
}
@Override
public boolean isInstanceType() {
// The universal constructor is its own instance, bizarrely.
return equals(registry.getNativeType(U2U_CONSTRUCTOR_TYPE));
}
@Override
public boolean isConstructor() {
return kind == Kind.CONSTRUCTOR;
}
@Override
public boolean isInterface() {
return kind == Kind.INTERFACE;
}
@Override
public boolean isOrdinaryFunction() {
return kind == Kind.ORDINARY;
}
@Override
public boolean isFunctionType() {
return true;
}
@Override
public boolean canBeCalled() {
return true;
}
public Iterable<Node> getParameters() {
Node n = getParametersNode();
if (n != null) {
return n.children();
} else {
return Collections.emptySet();
}
}
/** Gets an LP node that contains all params. May be null. */
public Node getParametersNode() {
return call == null ? null : call.parameters;
}
/** Gets the minimum number of arguments that this function requires. */
public int getMinArguments() {
// NOTE(nicksantos): There are some native functions that have optional
// parameters before required parameters. This algorithm finds the position
// of the last required parameter.
int i = 0;
int min = 0;
for (Node n : getParameters()) {
i++;
if (!n.isOptionalArg() && !n.isVarArgs()) {
min = i;
}
}
return min;
}
/**
* Gets the maximum number of arguments that this function requires,
* or Integer.MAX_VALUE if this is a variable argument function.
*/
public int getMaxArguments() {
Node params = getParametersNode();
if (params != null) {
Node lastParam = params.getLastChild();
if (lastParam == null || !lastParam.isVarArgs()) {
return params.getChildCount();
}
}
return Integer.MAX_VALUE;
}
public JSType getReturnType() {
return call == null ? null : call.returnType;
}
/**
* Gets the {@code prototype}
Closure, 142
<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public void enterScope(NodeTraversal t) {
// TODO(user): We CAN do this in the global scope, just need to be
// careful when something is exported. Liveness uses bit-vector for live
// sets so I don't see compilation time will be a problem for running this
// pass in the global scope.
Scope scope = t.getScope();
if (scope.isGlobal()) {
return;
}
ControlFlowGraph<Node> cfg = t.getControlFlowGraph();
LiveVariablesAnalysis liveness =
new LiveVariablesAnalysis(cfg, scope, compiler);
// If the function has exactly 2 params, mark them as escaped. This is
// a work-around for an IE bug where it throws an exception if you
// write to the parameters of the callback in a sort(). See:
// http://code.google.com/p/closure-compiler/issues/detail?id=58
<CHANGES>
<CHANGEE>
liveness.analyze();
UndiGraph<Var, Void> interferenceGraph =
computeVariableNamesInterferenceGraph(
t, cfg, liveness.getEscapedLocals());
GraphColoring<Var, Void> coloring =
new GreedyGraphColoring<Var, Void>(interferenceGraph,
coloringTieBreaker);
coloring.color();
colorings.push(coloring);
}
@Override
public void exitScope(NodeTraversal t) {
<FILEE>
<FILEB>
builder.append(' ');
}
builder.append('*');
}
token = next();
continue;
case EOL:
if (option != WhitespaceOption.SINGLE_LINE) {
builder.append("\n");
}
ignoreStar = true;
token = next();
continue;
case ANNOTATION:
case EOC:
case EOF:
// When we're capturing a license block, annotations
// in the block are ok.
<CHANGES>
<CHANGEE>
String multilineText = builder.toString();
if (option != WhitespaceOption.PRESERVE) {
multilineText = multilineText.trim();
}
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
if (multilineText.length() > 0) {
jsdocBuilder.markText(mult<SCANS> new StringBuilder(32);
b.append("function (");
int paramNum = (call == null || call.parameters == null) ?
0 : call.parameters.getChildCount();
boolean hasKnownTypeOfThis = !typeOfThis.isUnknownType();
if (hasKnownTypeOfThis) {
b.append("this:");
b.append(typeOfThis.toString());
}
if (paramNum > 0) {
if (hasKnownTypeOfThis) {
b.append(", ");
}
Node p = call.parameters.getFirstChild();
if (p.isVarArgs()) {
appendVarArgsString(b, p.getJSType());
} else {
b.append(p.getJSType().toString());
}
p = p.getNext();
while (p != null) {
b.append(", ");
if (p.isVarArgs()) {
appendVarArgsString(b, p.getJSType());
} else {
b.append(p.getJSType().toString());
}
p = p.getNext();
}
}
b.append(")");
if (call != null && call.returnType != null) {
b.append(": ");
b.append(call.returnType);
}
return b.toString();
}
/** Gets the string representation of a var args param. */
private void appendVarArgsString(StringBuilder builder, JSType paramType) {
if (paramType.isUnionType()) {
// Remove the optionalness from the var arg.
paramType = ((UnionType) paramType).getRestrictedUnion(
registry.getNativeType(JSTypeNative.VOID_TYPE));
}
builder.append("...[").append(paramType.toString()).append("]");
}
/**
* A function is a subtype of another if their call methods are related via
* subtyping and {@code this} is a subtype of {@code that} with regard to
* the prototype chain.
*/
@Override
public boolean isSubtype(JSType that) {
if (this.equals(that)) {
return true;
}
if (that.isFunctionType()) {
if (((FunctionType) that).isInterface()) {
// Any function can be assigned to an interface function.
return true;
}
if (this